1
0
mirror of https://git.zap.org.au/git/trader.git synced 2024-06-30 19:45:23 +00:00

Completely rewrite the scramble() and unscramble() functions

The file output is now completely in ASCII---and will be much harder to
break for casual cheating!
This commit is contained in:
John Zaitseff 2014-05-23 18:56:24 +10:00
parent 3d20e0b307
commit 9cb3a46693
4 changed files with 573 additions and 131 deletions

View File

@ -32,32 +32,22 @@
/************************************************************************
* Module-specific constants and macros *
* Module-specific macros *
************************************************************************/
// Game loading and saving constants
static const unsigned char game_file_crypt_key[] = {
0x50, 0x52, 0x55, 0x59, 0x5A, 0x5C, 0x5F,
0x90, 0x92, 0x95, 0x99, 0x9A, 0x9C, 0x9F,
0xA0, 0xA2, 0xA5, 0xA9, 0xAA, 0xAC, 0xAF,
0xD0, 0xD2, 0xD5, 0xD9, 0xDA, 0xDC, 0xDF
};
#define GAME_FILE_CRYPT_KEY_SIZE (sizeof(game_file_crypt_key) / sizeof(game_file_crypt_key[0]))
// Macros used in load_game()
#define load_game_scanf(_fmt, _var, _cond) \
do { \
if (fgets(bigbuf, BIGBUFSIZE, file) == NULL) { \
if (fgets(inbuf, BIGBUFSIZE, file) == NULL) { \
err_exit(_("%s: missing field on line %d"), \
filename, lineno); \
} \
if (sscanf(unscramble(&crypt_key, bigbuf, BIGBUFSIZE, \
buf, BUFSIZE), _fmt "\n", &(_var)) \
!= 1) { \
if (unscramble(buf, inbuf, BUFSIZE, crypt_key_p) == NULL) { \
err_exit(_("%s: illegal field on line %d"), \
filename, lineno); \
} \
if (sscanf(buf, _fmt "\n", &(_var)) != 1) { \
err_exit(_("%s: illegal field on line %d: `%s'"), \
filename, lineno, buf); \
} \
@ -89,12 +79,15 @@ static const unsigned char game_file_crypt_key[] = {
char *s; \
int len; \
\
if (fgets(bigbuf, BIGBUFSIZE, file) == NULL) { \
if (fgets(inbuf, BIGBUFSIZE, file) == NULL) { \
err_exit(_("%s: missing field on line %d"), \
filename, lineno); \
} \
if (strlen(unscramble(&crypt_key, bigbuf, BIGBUFSIZE, \
buf, BUFSIZE)) == 0) { \
if (unscramble(buf, inbuf, BUFSIZE, crypt_key_p) == NULL) { \
err_exit(_("%s: illegal field on line %d"), \
filename, lineno); \
} \
if (strlen(buf) == 0) { \
err_exit(_("%s: illegal value on line %d"), \
filename, lineno); \
} \
@ -129,12 +122,15 @@ static const unsigned char game_file_crypt_key[] = {
char *s; \
int len; \
\
if (fgets(bigbuf, BIGBUFSIZE, file) == NULL) { \
if (fgets(inbuf, BIGBUFSIZE, file) == NULL) { \
err_exit(_("%s: missing field on line %d"), \
filename, lineno); \
} \
if (strlen(unscramble(&crypt_key, bigbuf, BIGBUFSIZE, \
buf, BUFSIZE)) == 0) { \
if (unscramble(buf, inbuf, BUFSIZE, crypt_key_p) == NULL) { \
err_exit(_("%s: illegal field on line %d"), \
filename, lineno); \
} \
if (strlen(buf) == 0) { \
err_exit(_("%s: illegal value on line %d"), \
filename, lineno); \
} \
@ -160,8 +156,8 @@ static const unsigned char game_file_crypt_key[] = {
#define save_game_printf(_fmt, _var) \
do { \
snprintf(buf, BUFSIZE, _fmt "\n", _var); \
scramble(&crypt_key, buf, BUFSIZE, bigbuf, BIGBUFSIZE); \
fprintf(file, "%s", bigbuf); \
scramble(encbuf, buf, BIGBUFSIZE, crypt_key_p); \
fprintf(file, "%s", encbuf); \
} while (0)
#define save_game_write_int(_var) \
@ -227,11 +223,12 @@ bool load_game (int num)
int saved_errno, lineno;
char *prev_locale;
char *buf, *bigbuf;
char *buf, *inbuf;
wchar_t *wcbuf;
unsigned char crypt_key;
int crypt_key_input;
unsigned int crypt_key;
unsigned int *crypt_key_p;
int is_encrypted_input;
int n, i, j;
#ifdef USE_UTF8_GAME_FILE
@ -243,7 +240,7 @@ bool load_game (int num)
assert(num >= 1 && num <= 9);
buf = xmalloc(BUFSIZE);
bigbuf = xmalloc(BIGBUFSIZE);
inbuf = xmalloc(BIGBUFSIZE);
wcbuf = xmalloc(BUFSIZE * sizeof(wchar_t));
filename = game_filename(num);
@ -272,7 +269,7 @@ bool load_game (int num)
}
free(buf);
free(bigbuf);
free(inbuf);
free(wcbuf);
free(filename);
return false;
@ -341,13 +338,15 @@ bool load_game (int num)
lineno = 4;
// Read in the game file encryption key
if (fscanf(file, "%i\n", &crypt_key_input) != 1) {
// Read in the game file encryption status
if (fscanf(file, "%i\n", &is_encrypted_input) != 1) {
err_exit(_("%s: illegal or missing field on line %d"), filename, lineno);
}
crypt_key = (unsigned char) crypt_key_input;
lineno++;
crypt_key = 0;
crypt_key_p = is_encrypted_input ? &crypt_key : NULL;
// Read in various game variables
load_game_read_int(n, n == MAX_X);
load_game_read_int(n, n == MAX_Y);
@ -384,11 +383,13 @@ bool load_game (int num)
// Read in galaxy map
for (int x = 0; x < MAX_X; x++) {
if (fgets(bigbuf, BIGBUFSIZE, file) == NULL) {
if (fgets(inbuf, BIGBUFSIZE, file) == NULL) {
err_exit(_("%s: missing field on line %d"), filename, lineno);
}
if (strlen(unscramble(&crypt_key, bigbuf, BIGBUFSIZE, buf, BUFSIZE))
!= MAX_Y + 1) {
if (unscramble(buf, inbuf, BUFSIZE, crypt_key_p) == NULL) {
err_exit(_("%s: illegal field on line %d"), filename, lineno);
}
if (strlen(buf) != MAX_Y + 1) {
err_exit(_("%s: illegal field on line %d"), filename, lineno);
}
@ -422,7 +423,7 @@ bool load_game (int num)
#endif
free(buf);
free(bigbuf);
free(inbuf);
free(wcbuf);
free(filename);
free(prev_locale);
@ -437,15 +438,16 @@ bool load_game (int num)
bool save_game (int num)
{
const char *data_dir;
char *buf, *bigbuf;
char *buf, *encbuf;
char *filename;
FILE *file;
char *codeset;
int saved_errno;
char *prev_locale;
struct stat statbuf;
unsigned char crypt_key;
int i, j, x, y;
unsigned int crypt_key;
unsigned int *crypt_key_p;
#ifdef USE_UTF8_GAME_FILE
iconv_t icd;
@ -456,10 +458,10 @@ bool save_game (int num)
assert(num >= 1 && num <= 9);
buf = xmalloc(BUFSIZE);
bigbuf = xmalloc(BIGBUFSIZE);
encbuf = xmalloc(BIGBUFSIZE);
crypt_key = option_dont_encrypt ? 0 :
game_file_crypt_key[randi(GAME_FILE_CRYPT_KEY_SIZE)];
crypt_key = 0;
crypt_key_p = option_dont_encrypt ? NULL : &crypt_key;
// Create the data directory, if needed
data_dir = data_directory();
@ -480,7 +482,7 @@ bool save_game (int num)
strerror(saved_errno));
free(buf);
free(bigbuf);
free(encbuf);
return false;
}
}
@ -501,7 +503,7 @@ bool save_game (int num)
"^{File %s: %s^}"), num, filename, strerror(saved_errno));
free(buf);
free(bigbuf);
free(encbuf);
free(filename);
return false;
}
@ -534,9 +536,9 @@ bool save_game (int num)
prev_locale = xstrdup(setlocale(LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "C");
// Write out the game file header and encryption key
// Write out the game file header and encryption status
fprintf(file, "%s\n" "%s\n", GAME_FILE_HEADER, GAME_FILE_API_VERSION);
fprintf(file, "%s\n" "%d\n", codeset, crypt_key);
fprintf(file, "%s\n" "%d\n", codeset, ! option_dont_encrypt);
// Write out various game variables
save_game_write_int(MAX_X);
@ -581,8 +583,8 @@ bool save_game (int num)
*p++ = '\n';
*p = '\0';
scramble(&crypt_key, buf, BUFSIZE, bigbuf, BIGBUFSIZE);
fprintf(file, "%s", bigbuf);
scramble(encbuf, buf, BIGBUFSIZE, crypt_key_p);
fprintf(file, "%s", encbuf);
}
// Write out a dummy sentinal value
@ -602,7 +604,7 @@ bool save_game (int num)
#endif
free(buf);
free(bigbuf);
free(encbuf);
free(filename);
free(prev_locale);
return true;

View File

@ -53,7 +53,7 @@
************************************************************************/
#define GAME_FILE_HEADER "Star Traders Saved Game"
#define GAME_FILE_API_VERSION "File API 7.2" // For game loads and saves
#define GAME_FILE_API_VERSION "File API 7.5" // For game loads and saves
#define GAME_FILE_SENTINEL 42 // End of game file sentinel
#ifdef USE_UTF8_GAME_FILE
@ -62,7 +62,7 @@
#endif
#define BUFSIZE 1024 // For various string buffers
#define BIGBUFSIZE 4096 // For buffers known to be larger
#define BIGBUFSIZE 2048 // For buffers known to be larger
#endif /* included_TRADER_H */

View File

@ -50,7 +50,7 @@ wchar_t *mon_thousands_sep; // Local monetary thousands separator
/************************************************************************
* Module-specific constants and variable definitions *
* Module-specific constants and macros *
************************************************************************/
#define GAME_FILENAME_PROTO "game%d"
@ -63,25 +63,120 @@ wchar_t *mon_thousands_sep; // Local monetary thousands separator
#define MOD_POSIX_P_SEP_BY_SPACE 0
// Constants used for scrambling and unscrambling game data
#define SCRAMBLE_PAD_CHAR '.'
#define UNSCRAMBLE_INVALID (-1)
#define UNSCRAMBLE_PAD_CHAR (-2)
#define SCRAMBLE_CRC_LEN 8 // Length of CRC in ASCII (excl NUL)
#define SCRAMBLE_CHKSUM_LEN 3 // For checksum, excluding NUL byte
#define SCRAMBLE_CRC_MASK 0xFFFFFFFF // Bits of CRC to keep
#define SCRAMBLE_CHKSUM_MASK 0x0FFF // Bits of checksum to keep
static const char scramble_index[] =
#define SCRAMBLE_PAD_CHAR '*'
#define SCRAMBLE_IGNORE_CHAR '~'
#define UNSCRAMBLE_INVALID (-1)
#define UNSCRAMBLE_IGNORE (-2)
#define UNSCRAMBLE_PAD_CHAR (-3)
static const char scramble_table[] =
"0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz-_";
static const char unscramble_index[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -2, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, -1, -1, -1, -1, 63,
-1, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39,
41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, -1, -1, -1, -1, -1
#define _b(n) \
((n) == '0' ? 0 : (n) == '1' ? 1 : (n) == '2' ? 2 : (n) == '3' ? 3 : \
(n) == '4' ? 4 : (n) == '5' ? 5 : (n) == '6' ? 6 : (n) == '7' ? 7 : \
(n) == '8' ? 8 : (n) == '9' ? 9 : (n) == 'A' ? 10 : (n) == 'a' ? 11 : \
(n) == 'B' ? 12 : (n) == 'b' ? 13 : (n) == 'C' ? 14 : (n) == 'c' ? 15 : \
(n) == 'D' ? 16 : (n) == 'd' ? 17 : (n) == 'E' ? 18 : (n) == 'e' ? 19 : \
(n) == 'F' ? 20 : (n) == 'f' ? 21 : (n) == 'G' ? 22 : (n) == 'g' ? 23 : \
(n) == 'H' ? 24 : (n) == 'h' ? 25 : (n) == 'I' ? 26 : (n) == 'i' ? 27 : \
(n) == 'J' ? 28 : (n) == 'j' ? 29 : (n) == 'K' ? 30 : (n) == 'k' ? 31 : \
(n) == 'L' ? 32 : (n) == 'l' ? 33 : (n) == 'M' ? 34 : (n) == 'm' ? 35 : \
(n) == 'N' ? 36 : (n) == 'n' ? 37 : (n) == 'O' ? 38 : (n) == 'o' ? 39 : \
(n) == 'P' ? 40 : (n) == 'p' ? 41 : (n) == 'Q' ? 42 : (n) == 'q' ? 43 : \
(n) == 'R' ? 44 : (n) == 'r' ? 45 : (n) == 'S' ? 46 : (n) == 's' ? 47 : \
(n) == 'T' ? 48 : (n) == 't' ? 49 : (n) == 'U' ? 50 : (n) == 'u' ? 51 : \
(n) == 'V' ? 52 : (n) == 'v' ? 53 : (n) == 'W' ? 54 : (n) == 'w' ? 55 : \
(n) == 'X' ? 56 : (n) == 'x' ? 57 : (n) == 'Y' ? 58 : (n) == 'y' ? 59 : \
(n) == 'Z' ? 60 : (n) == 'z' ? 61 : (n) == '-' ? 62 : (n) == '_' ? 63 : \
(n) == ' ' ? UNSCRAMBLE_IGNORE : \
(n) == '\t' ? UNSCRAMBLE_IGNORE : \
(n) == '\n' ? UNSCRAMBLE_IGNORE : \
(n) == '\r' ? UNSCRAMBLE_IGNORE : \
(n) == SCRAMBLE_IGNORE_CHAR ? UNSCRAMBLE_IGNORE : \
(n) == SCRAMBLE_PAD_CHAR ? UNSCRAMBLE_PAD_CHAR : \
UNSCRAMBLE_INVALID)
static const signed char unscramble_table[] = {
_b(0), _b(1), _b(2), _b(3), _b(4), _b(5), _b(6), _b(7),
_b(8), _b(9), _b(10) , _b(11), _b(12), _b(13), _b(14), _b(15),
_b(16), _b(17), _b(18), _b(19), _b(20), _b(21), _b(22), _b(23),
_b(24), _b(25), _b(26), _b(27), _b(28), _b(29), _b(30), _b(31),
_b(32), _b(33), _b(34), _b(35), _b(36), _b(37), _b(38), _b(39),
_b(40), _b(41), _b(42), _b(43), _b(44), _b(45), _b(46), _b(47),
_b(48), _b(49), _b(50), _b(51), _b(52), _b(53), _b(54), _b(55),
_b(56), _b(57), _b(58), _b(59), _b(60), _b(61), _b(62), _b(63),
_b(64), _b(65), _b(66), _b(67), _b(68), _b(69), _b(70), _b(71),
_b(72), _b(73), _b(74), _b(75), _b(76), _b(77), _b(78), _b(79),
_b(80), _b(81), _b(82), _b(83), _b(84), _b(85), _b(86), _b(87),
_b(88), _b(89), _b(90), _b(91), _b(92), _b(93), _b(94), _b(95),
_b(96), _b(97), _b(98), _b(99), _b(100), _b(101), _b(102), _b(103),
_b(104), _b(105), _b(106), _b(107), _b(108), _b(109), _b(110), _b(111),
_b(112), _b(113), _b(114), _b(115), _b(116), _b(117), _b(118), _b(119),
_b(120), _b(121), _b(122), _b(123), _b(124), _b(125), _b(126), _b(127),
_b(128), _b(129), _b(130), _b(131), _b(132), _b(133), _b(134), _b(135),
_b(136), _b(137), _b(138), _b(139), _b(140), _b(141), _b(142), _b(143),
_b(144), _b(145), _b(146), _b(147), _b(148), _b(149), _b(150), _b(151),
_b(152), _b(153), _b(154), _b(155), _b(156), _b(157), _b(158), _b(159),
_b(160), _b(161), _b(162), _b(163), _b(164), _b(165), _b(166), _b(167),
_b(168), _b(169), _b(170), _b(171), _b(172), _b(173), _b(174), _b(175),
_b(176), _b(177), _b(178), _b(179), _b(180), _b(181), _b(182), _b(183),
_b(184), _b(185), _b(186), _b(187), _b(188), _b(189), _b(190), _b(191),
_b(192), _b(193), _b(194), _b(195), _b(196), _b(197), _b(198), _b(199),
_b(200), _b(201), _b(202), _b(203), _b(204), _b(205), _b(206), _b(207),
_b(208), _b(209), _b(210), _b(211), _b(212), _b(213), _b(214), _b(215),
_b(216), _b(217), _b(218), _b(219), _b(220), _b(221), _b(222), _b(223),
_b(224), _b(225), _b(226), _b(227), _b(228), _b(229), _b(230), _b(231),
_b(232), _b(233), _b(234), _b(235), _b(236), _b(237), _b(238), _b(239),
_b(240), _b(241), _b(242), _b(243), _b(244), _b(245), _b(246), _b(247),
_b(248), _b(249), _b(250), _b(251), _b(252), _b(253), _b(254), _b(255)
};
#define UNSCRAMBLE_INDEX_SIZE (sizeof(unscramble_index) / sizeof(unscramble_index[0]))
#define UNSCRAMBLE_TABLE_SIZE (sizeof(unscramble_table) / sizeof(unscramble_table[0]))
static const unsigned char xor_table[] = {
/* Set of bytes 0x00 to 0xFF in random order; each byte in an input
string is XORed with successive bytes in this table. */
0x00, 0xCE, 0xB1, 0x9F, 0xE4, 0xE0, 0xE3, 0x79,
0xA1, 0x3B, 0x4E, 0x89, 0x81, 0x84, 0x43, 0xC8,
0xBE, 0x0F, 0x67, 0x2A, 0xB4, 0xD8, 0xBA, 0x5D,
0x94, 0x06, 0x69, 0x0E, 0x1C, 0x48, 0x9E, 0x0A,
0x1D, 0x09, 0x02, 0xCD, 0xD4, 0xF6, 0x5B, 0x8A,
0xAE, 0x65, 0xB3, 0xB5, 0xA7, 0x13, 0x03, 0xF2,
0x42, 0xF0, 0xA6, 0xAA, 0x35, 0xCB, 0x2C, 0x55,
0xF5, 0xC7, 0x32, 0xB7, 0x6B, 0xEA, 0xC3, 0x6F,
0x41, 0xFF, 0xD1, 0x24, 0x54, 0xA9, 0xC6, 0xC2,
0x74, 0xEE, 0xBC, 0x99, 0x59, 0x71, 0x3D, 0x85,
0x0B, 0xF7, 0x3A, 0x7E, 0xDB, 0x45, 0xE8, 0x96,
0xD0, 0xC1, 0xE6, 0xFD, 0x86, 0x8C, 0x9B, 0x0C,
0x66, 0x5F, 0xE5, 0x14, 0x98, 0x3C, 0xBD, 0xE2,
0x88, 0xA3, 0x30, 0x38, 0x2F, 0xA2, 0x37, 0x70,
0xB8, 0x11, 0x61, 0x93, 0x52, 0x1B, 0xDD, 0x20,
0x60, 0x19, 0xEF, 0xD2, 0xEC, 0x73, 0x07, 0x92,
0x4C, 0x6A, 0xA8, 0x9D, 0x34, 0x04, 0x87, 0x2E,
0x1E, 0xA4, 0xCA, 0x72, 0x63, 0xD7, 0x7F, 0xFB,
0x68, 0xE1, 0xBF, 0x10, 0x8E, 0xAF, 0x9A, 0xFA,
0xA0, 0xDE, 0x1F, 0x31, 0x15, 0x97, 0xED, 0x2B,
0x36, 0x8D, 0x12, 0xC5, 0x23, 0x95, 0x33, 0x56,
0x4F, 0xE7, 0xAD, 0x5C, 0x4B, 0x83, 0xDC, 0x29,
0xE9, 0xCF, 0x8F, 0x58, 0x4D, 0x5A, 0x08, 0x49,
0xFC, 0x6D, 0x7C, 0xB6, 0xD3, 0x7B, 0xD6, 0x53,
0x57, 0x82, 0x0D, 0xD9, 0x7D, 0xDA, 0x4A, 0xDF,
0x27, 0x40, 0x1A, 0x22, 0xC9, 0x51, 0x3E, 0x6C,
0xC4, 0x18, 0xCC, 0xAC, 0xEB, 0xA5, 0xF4, 0x44,
0xFE, 0x76, 0xF8, 0x75, 0xF3, 0x2D, 0xB0, 0xB9,
0x9C, 0x47, 0x7A, 0x28, 0xBB, 0xF1, 0x16, 0x64,
0x46, 0x21, 0x78, 0x90, 0xD5, 0x80, 0x3F, 0x39,
0x25, 0xB2, 0x6E, 0x8B, 0x77, 0xC0, 0x05, 0x50,
0x17, 0xF9, 0x01, 0x26, 0x91, 0x5E, 0x62, 0xAB
};
#define XOR_TABLE_SIZE (sizeof(xor_table) / sizeof(xor_table[0]))
/************************************************************************
@ -94,6 +189,69 @@ static char *data_directory_str = NULL; // Writable data dir pathname
static bool add_currency_symbol = false; // Do we need to add "$"?
/************************************************************************
* Module-specific function prototypes *
************************************************************************/
/*
Function: apply_xor - Scramble a buffer using xor_table
Parameters: dest - Location of destination buffer
src - Location of source buffer
n - Number of bytes to scramble
key - Pointer to xor_table index
Returns: (nothing)
This function copies n bytes from *src into *dest, applying a XOR with
the contents of xor_table in the process. It is a reversable function:
apply_xor(apply_xor(buffer)) == buffer. It is used by both scramble()
and unscramble().
*/
static void apply_xor (void *restrict dest, const void *restrict src,
size_t n, unsigned int *restrict key);
/*
Function: b64encode - Convert a block to non-standard Base64 encoding
Parameters: in - Location of input buffer
inlen - Size of input buffer
out - Location of output buffer
outlen - Size of output buffer
Returns: size_t - Number of bytes placed in output buffer
This function encodes inlen bytes in the input buffer into the output
buffer using a non-standard Base64 encoding (as contained above in
scramble_table[]). The resulting encoded string length is returned
(including trailing '\n' but NOT including trailing NUL).
Note that the output buffer must be at least 4/3 the size of the input
buffer; if not, an assert is generated.
This function is used by scramble().
*/
static size_t b64encode (const void *restrict in, size_t inlen,
void *restrict out, size_t outlen);
/*
Function: b64decode - Convert a block from non-standard Base64 encoding
Parameters: in - Location of input buffer
inlen - Size of input buffer
out - Location of output buffer
outlen - Size of output buffer
Returns: ssize_t - Number of bytes placed in output buffer, or -1
This function decodes up to inlen bytes in the input buffer into the
output buffer using a non-standard Base64 encoding (as contained above
in unscramble_table[]). The resulting decoded buffer length is
returned; that buffer may contain NUL bytes. If an error occurs during
decoding, -1 is returned instead.
This function is used by unscramble().
*/
static ssize_t b64decode (const void *restrict in, size_t inlen,
void *restrict out, size_t outlen);
/************************************************************************
* Initialisation and environment function definitions *
************************************************************************/
@ -436,53 +594,336 @@ ssize_t l_strfmon (char *restrict buf, size_t maxsize,
* Encryption function definitions *
************************************************************************/
// These functions are documented in the file "utils.h"
/* These functions are documented in the file "utils.h" or in the
comments above. */
/***********************************************************************/
// scramble: Scramble (encrypt) the buffer
char *scramble (unsigned char *restrict key,
char *restrict inbuf, int inbufsize,
char *restrict outbuf, int outbufsize)
char *scramble (char *restrict dest, const char *restrict src,
size_t size, unsigned int *restrict key)
{
/* The algorithm used here is reversable: scramble(scramble(...))
will (or, at least, should!) return the same as the original
buffer. Problematic characters are ignored; however, this
function assumes all other characters are permitted in files.
This is true on all POSIX systems. */
unsigned long int crc;
unsigned int chksum;
size_t srclen;
char *xorbuf, *midxor;
char *middest;
char crcbuf[SCRAMBLE_CRC_LEN + 1];
char chksumbuf[SCRAMBLE_CHKSUM_LEN + 1];
assert(outbuf != NULL);
if (inbuf != NULL && key != NULL && *key != 0) {
char *p = inbuf;
unsigned char k = ~*key;
assert(dest != NULL);
assert(src != NULL);
assert(size > 0);
for (int i = 0; i < inbufsize && *p != '\0'; i++, k++, p++) {
char c = *p;
char r = c ^ k; // Simple encryption: XOR on a moving key
srclen = strlen(src);
if (c != '\r' && c != '\n'
&& r != '\r' && r != '\n' && r != '\0') {
*p = r;
}
if (key == NULL) {
// No encryption required
assert(size >= srclen + 2); // Enough room to add "\n"?
strcpy(dest, src);
// Add "\n" if needed
if (dest[srclen - 1] != '\n') {
dest[srclen] = '\n';
dest[srclen + 1] = '\0';
}
} else {
// Scramble the input
xorbuf = xmalloc(srclen + SCRAMBLE_CRC_LEN + 1);
// Scramble src using *key, leaving room for CRC32 in front
midxor = xorbuf + SCRAMBLE_CRC_LEN;
apply_xor(midxor, src, srclen, key);
// Calculate CRC32 checksum of XORed buffer
crc = crc32(midxor, srclen) & SCRAMBLE_CRC_MASK;
snprintf(crcbuf, SCRAMBLE_CRC_LEN + 1, "%08lx", crc);
memcpy(xorbuf, crcbuf, SCRAMBLE_CRC_LEN);
// Encode whole buffer (including CRC32) using Base64
middest = dest + SCRAMBLE_CHKSUM_LEN;
b64encode(xorbuf, srclen + SCRAMBLE_CRC_LEN,
middest, size - SCRAMBLE_CHKSUM_LEN);
// Calculate simple checksum
chksum = 0;
for (char *p = middest; *p != '\0' && *p != '\n'; p++) {
chksum += *p;
}
chksum &= SCRAMBLE_CHKSUM_MASK;
// Place checksum in front of Base64 string
snprintf(chksumbuf, SCRAMBLE_CHKSUM_LEN + 1, "%03x", chksum);
memcpy(dest, chksumbuf, SCRAMBLE_CHKSUM_LEN);
free(xorbuf);
}
strcpy(outbuf, inbuf);
return outbuf;
return dest;
}
/***********************************************************************/
// unscramble: Unscramble (decrypt) the buffer
char *unscramble (unsigned char *restrict key,
char *restrict inbuf, int inbufsize,
char *restrict outbuf, int outbufsize)
char *unscramble (char *restrict dest, const char *restrict src,
size_t size, unsigned int *restrict key)
{
return scramble(key, inbuf, inbufsize, outbuf, outbufsize);
unsigned long int crc, crc_input;
unsigned int chksum, chksum_input;
size_t srclen;
char *xorbuf, *midxor;
ssize_t xorlen;
const char *midsrc;
char crcbuf[SCRAMBLE_CRC_LEN + 2]; // Leave room for '\n\0'
char chksumbuf[SCRAMBLE_CHKSUM_LEN + 2];
assert(dest != NULL);
assert(src != NULL);
assert(size > 0);
srclen = strlen(src);
if (key == NULL) {
// No decryption required
assert(size >= srclen + 1);
strcpy(dest, src);
} else {
// Unscramble the input
// Copy out simple checksum from input
memcpy(chksumbuf, src, SCRAMBLE_CHKSUM_LEN);
chksumbuf[SCRAMBLE_CHKSUM_LEN] = '\n';
chksumbuf[SCRAMBLE_CHKSUM_LEN + 1] = '\0';
if (sscanf(chksumbuf, "%x\n", &chksum_input) != 1) {
return NULL;
}
// Calculate and compare checksums
midsrc = src + SCRAMBLE_CHKSUM_LEN;
chksum = 0;
for (const char *p = midsrc; *p != '\0' && *p != '\n'; p++) {
chksum += *p;
}
chksum &= SCRAMBLE_CHKSUM_MASK;
if (chksum != chksum_input) {
return NULL;
}
xorbuf = xmalloc(size + SCRAMBLE_CRC_LEN);
// Decode buffer sans checksum using Base64
xorlen = b64decode(midsrc, srclen - SCRAMBLE_CHKSUM_LEN,
xorbuf, size + SCRAMBLE_CRC_LEN);
if (xorlen < SCRAMBLE_CRC_LEN) {
free(xorbuf);
return NULL;
}
// Copy out CRC32 checksum
memcpy(crcbuf, xorbuf, SCRAMBLE_CRC_LEN);
crcbuf[SCRAMBLE_CRC_LEN] = '\n';
crcbuf[SCRAMBLE_CRC_LEN + 1] = '\0';
if (sscanf(crcbuf, "%lx\n", &crc_input) != 1) {
free(xorbuf);
return NULL;
}
// Calculate and compare CRC32 checksums
midxor = xorbuf + SCRAMBLE_CRC_LEN;
crc = crc32(midxor, xorlen - SCRAMBLE_CRC_LEN) & SCRAMBLE_CRC_MASK;
if (crc != crc_input) {
free(xorbuf);
return NULL;
}
// Descramble xorbuf using *key, ignoring CRC32 in front
apply_xor(dest, midxor, xorlen - SCRAMBLE_CRC_LEN, key);
// Convert the output to a C string
assert(size >= xorlen - SCRAMBLE_CRC_LEN + 1);
dest[xorlen - SCRAMBLE_CRC_LEN] = '\0';
free(xorbuf);
}
return dest;
}
/***********************************************************************/
// apply_xor: Scramble a buffer using xor_table
void apply_xor (void *restrict dest, const void *restrict src,
size_t n, unsigned int *restrict key)
{
assert(dest != NULL);
assert(src != NULL);
assert(key != NULL);
assert(*key < XOR_TABLE_SIZE);
for (size_t i = 0; i < n; i++, dest++, src++) {
*(unsigned char *) dest = *(unsigned char *) src ^ xor_table[*key];
*key = (*key + 1) % XOR_TABLE_SIZE;
}
}
/***********************************************************************/
// b64encode: Convert a block to non-standard Base64 encoding
size_t b64encode (const void *restrict in, size_t inlen,
void *restrict out, size_t outlen)
{
size_t count;
size_t padding;
// Note that bit manipulations on strings require unsigned char!
const unsigned char *u_in = in;
unsigned char *u_out = out;
assert(u_in != NULL);
assert(u_out != NULL);
assert(outlen > 0);
assert(outlen > inlen);
count = 0;
padding = inlen % 3;
for (size_t i = 0; i < inlen; i += 3, u_in += 3) {
unsigned long int n;
unsigned char n0, n1, n2, n3;
// Convert three input bytes into a 24-bit number
n = u_in[0] << 16;
if (i + 1 < inlen) {
n += u_in[1] << 8;
}
if (i + 2 < inlen) {
n += u_in[2];
}
// Convert the 24-bit number into four Base64 bytes
n0 = (unsigned char) (n >> 18) & 0x3F;
n1 = (unsigned char) (n >> 12) & 0x3F;
n2 = (unsigned char) (n >> 6) & 0x3F;
n3 = (unsigned char) n & 0x3F;
assert(count + 3 < outlen);
*u_out++ = scramble_table[n0];
*u_out++ = scramble_table[n1];
count += 2;
if (i + 1 < inlen) {
*u_out++ = scramble_table[n2];
count++;
}
if (i + 2 < inlen) {
*u_out++ = scramble_table[n3];
count++;
}
}
if (padding > 0) {
assert(count + 2 < outlen);
for (; padding < 3; padding++) {
*u_out++ = SCRAMBLE_PAD_CHAR;
count++;
}
}
assert(count + 2 <= outlen);
*u_out++ = '\n';
*u_out = '\0';
count++;
return count;
}
/***********************************************************************/
// b64decode: Convert a block from non-standard Base64 encoding
ssize_t b64decode (const void *restrict in, size_t inlen,
void *restrict out, size_t outlen)
{
size_t count;
unsigned long int n;
// Note that bit manipulations on strings require unsigned char!
// Using char * results in very subtle bugs indeed...
const unsigned char *u_in = in;
unsigned char *u_out = out;
assert(u_in != NULL);
assert(u_out != NULL);
assert(outlen > 0);
count = 0;
n = 1;
for (size_t i = 0; i < inlen && *u_in != '\0'; i++, u_in++) {
char c = *u_in > UNSCRAMBLE_TABLE_SIZE ?
UNSCRAMBLE_INVALID : unscramble_table[*u_in];
switch (c) {
case UNSCRAMBLE_INVALID:
return -1;
case UNSCRAMBLE_IGNORE:
continue;
case UNSCRAMBLE_PAD_CHAR:
// Assume end of data
i = inlen;
continue;
default:
n = n << 6 | c; // c is 0 .. 63
if (n & 0x1000000) {
// Convert 24-bit number into three output bytes
count += 3;
if (count > outlen) {
return -1;
}
*u_out++ = n >> 16;
*u_out++ = n >> 8;
*u_out++ = n;
n = 1;
}
}
}
if (n & 0x40000) {
count += 2;
if (count > outlen) {
return -1;
}
*u_out++ = n >> 10;
*u_out++ = n >> 2;
} else if (n & 0x1000) {
count += 1;
if (count > outlen) {
return -1;
}
*u_out++ = n >> 4;
}
return count;
}

View File

@ -260,59 +260,58 @@ extern ssize_t l_strfmon (char *restrict buf, size_t maxsize,
************************************************************************/
/*
The functions described here are simple in the extreme: they are only
designed to stop casual cheating!
The functions described here are NOT cryptographically secure: they are
only designed to stop casual cheating!
*/
/*
Function: scramble - Scramble (encrypt) the buffer
Parameters: key - Pointer to encryption/decryption key
inbuf - Pointer to input buffer to encrypt
inbufsize - Size of input buffer
outbuf - Pointer to output buffer
outbufsize - Size of output buffer
Returns: char * - Pointer to output buffer
Function: scramble - Scramble (encrypt) the buffer
Parameters: dest - Pointer to output buffer
src - Pointer to input buffer to encrypt
size - Size of output buffer
key - Pointer to encryption/decryption key
Returns: char * - Pointer to output buffer
This function scrambles (encrypts) the buffer *inbuf using a trivial
encryption algorithm and places the result in *outbuf. If key is NULL
or *key is zero, no encryption takes place: the input buffer is copied
to the output buffer as-is.
This function scrambles (encrypts) the buffer *src and places the
result in *dest. It uses *key to keep a running encryption key. If
the key is NULL, no encryption is performed.
The input buffer should contain a C-style string terminated by '\0'.
The characters '\r', '\n' and '\0' are guaranteed to remain the same
before and after encryption. Note that inbuf and outbuf MUST point to
different buffers, and that outbuf typically must be four times larger
than inbuf. At most inbufsize bytes are encrypted; outbuf is returned
as the result.
The output buffer will be terminated with '\n\0', even if the input
does not have a terminating '\n'. The pointer dest is returned as the
output.
Note that src and dest MUST point to different buffers, and that *dest
typically must be twice as large as *src. In addition, *key MUST be
initialised to zero before calling scramble() for the first time.
*/
extern char *scramble (unsigned char *restrict key,
char *restrict inbuf, int inbufsize,
char *restrict outbuf, int outbufsize);
extern char *scramble (char *restrict dest, const char *restrict src,
size_t size, unsigned int *restrict key);
/*
Function: unscramble - Unscramble (decrypt) the buffer
Parameters: key - Pointer to encryption/decryption key
inbuf - Pointer to input buffer to decrypt
inbufsize - Size of input buffer
outbuf - Pointer to output buffer
outbufsize - Size of output buffer
Returns: char * - Pointer to output buffer
Parameters: dest - Pointer to output buffer
src - Pointer to input buffer to decrypt
size - Size of output buffer
key - Pointer to encryption/decryption key
Returns: char * - Pointer to output buffer or NULL on error
This function does the reverse of scramble(): it unscrambles (decrypts)
the buffer *inbuf using a trivial algorithm and places the result in
*outbuf. If key is NULL or *key is zero, no decryption takes place:
the input buffer is copied to the output buffer as-is.
the buffer *src and places the result in *dest. If key is NULL, no
decryption takes place: the input buffer is copied to the output buffer
without changes.
The buffer should contain a C-style string terminated by '\0'. As for
scramble(), the characters '\r', '\n' and '\0' will not be changed (nor
will any encrypted character map back to these values). Note that
inbuf and outbuf MUST point to different buffers. At most bufsize
bytes are decrypted; outbuf is returned as the result.
The buffer should contain a C-style string terminated by '\0'. Note
that src and dest MUST point to different buffers. The pointer dest is
returned as the output, unless there is an error in the data (such as a
corrupted checksum), in which case NULL is returned.
Note that *key MUST be initialised to zero before calling unscramble()
for the first time.
*/
extern char *unscramble (unsigned char *restrict key,
char *restrict inbuf, int inbufsize,
char *restrict outbuf, int outbufsize);
extern char *unscramble (char *restrict dest, const char *restrict src,
size_t size, unsigned int *restrict key);
/************************************************************************