This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* decodex.c - Decode64-decrypt-decompress. (Sibling of encodex.c) | |
This program demonstrates the method used by notex and similar | |
programs to decrypt text. The program is not a complete cryptographic | |
application: in particular, note the absence of any clean-up in case | |
of abnormal termination and a rather unsecure method of key-entry. | |
See the preamble of encodex.c for a complete discussion of the text | |
format and C language code. | |
Programmer: Hrvoje Lukatela, 2002 | |
*/ | |
#include <string.h> | |
#include <stdio.h> | |
#include "toolbox.h" | |
#define KEY_MIN 8 /* minimum key lenght */ | |
#define KEY_MAX 16 /* maximum key length */ | |
#define PASS_PHRASE_MAX 100 /* maximum passphrase length */ | |
#define CRYPT_BLOCK_INTS 2 | |
#define CRYPT_BLOCK_CHARS 8 | |
#define BASE64_BIN_LENGTH 48 | |
#define BASE64_ENCODED_LENGTH 64 | |
static void usage(char *, char *); | |
static unsigned char *nextData64Line(const unsigned char *, int, int *); | |
int main(int argc, char **argv) { | |
char inFn[FILENAME_MAX + 2]; | |
FILE *inFp; | |
char outFn[FILENAME_MAX + 2]; | |
FILE *outFp; | |
char keyStr[PASS_PHRASE_MAX + 2]; | |
unsigned char key[KEY_MAX]; | |
unsigned char *padA; | |
unsigned char *padB; | |
unsigned int cryptoPad[TB_BFPAD_SIZE]; /* Blowfish pad */ | |
int n, padSize; | |
int i, ixIn, ixOut, iBigEndian; | |
unsigned int fr[2]; /* feedback "register" */ | |
unsigned int cryptBlock[2]; /* one we operate on */ | |
unsigned int prevBlock[2]; /* one we operate on */ | |
unsigned char binLine[BASE64_BIN_LENGTH]; | |
int inTextLength, decompressedTextLength, compressedTextLength; | |
int cipherTextLength, plainTextLength; | |
unsigned int crc32in, crc32check; | |
unsigned char *puc; | |
int optch; | |
char *optval; | |
char *progfullname; | |
char *progname; | |
char *pa; | |
/* -------------------------------------------------------------------------- */ | |
progfullname = (char *)malloc(strlen(argv[0]) + 1); | |
strcpy(progfullname, argv[0]); | |
fprintf(stderr, "%s: decrypt text in textec format.\n", progfullname); | |
pa = strrchr(progfullname, TB_SUFFIX_SEPARATOR); | |
if (pa) *pa = '\0'; | |
pa = strrchr(progfullname, TB_DIR_SEPARATOR); | |
if (pa) progname = pa + 1; | |
else progname = progfullname; | |
*keyStr = '\0'; | |
iBigEndian = tb_IsBigEndian(); | |
if (argc < 2) usage(progname, NULL); | |
while ((optch = tb_CLineOption(argc, argv, &optval)) != -1) { | |
switch (optch) { | |
case 'h': case '?': usage(progname, NULL); break; | |
case 'k': strncpy(keyStr, optval, PASS_PHRASE_MAX); break; | |
default: usage(progname, "unexpected option"); | |
} | |
} | |
if (*keyStr == '\0') tb_ErrorExit(progname, NULL, NULL, __LINE__, "No key given (-k=...).\n"); | |
if (strlen(keyStr) < KEY_MIN) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Key or passphrase (%s) must be at least %d characters long.\n", keyStr, KEY_MIN); | |
pa = tb_CLineFileName(argc, argv); /* input file name */ | |
if (pa == NULL) tb_ErrorExit(progname, NULL, NULL, __LINE__, "No input file name given\n"); | |
strcpy(inFn, pa); | |
inFp = fopen(inFn, "rb"); | |
if (inFp == NULL) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Input file [%s] open error\n", inFn); | |
fseek(inFp, 0, SEEK_END); | |
inTextLength = ftell(inFp); | |
fseek(inFp, 0, SEEK_SET); | |
if (inTextLength == 0) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Zero-length file [%s]\n", inFn); | |
pa = tb_CLineFileName(argc, argv); /* output file name... */ | |
if (pa == NULL) { | |
strcpy(outFn, "standard output"); | |
outFp = stdout; | |
} | |
else { | |
strcpy(outFn, pa); | |
/* outFp = fopen(outFn, TB_TEXT_WRITE_OPEN); */ | |
outFp = fopen(outFn, "wb"); | |
} | |
if (outFp == NULL) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Can't open %s for writing\n", outFn); | |
/* Text will be ping-ponged between two "workpads" - A and B */ | |
padSize = 8 * inTextLength + 1024; /* max decompression factor */ | |
padA = (unsigned char *)malloc(padSize + 2); | |
padB = (unsigned char *)malloc(padSize + 2); | |
if ((padA == NULL) || (padB == NULL)) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Memory (%d) allocation error.\n", padSize + padSize); | |
/* Step 0: initialize Blowfish */ | |
n = strlen(keyStr); /* did we get a key or a passphrase? */ | |
if (n > KEY_MAX) { | |
tb_HashBytes((unsigned char *)keyStr, n, key); | |
n = KEY_MAX; | |
} | |
else memcpy(key, keyStr, n); | |
i = tb_BlowfishInitPad(cryptoPad, key, n); | |
if (i) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Blowfish initialization error %d.\n", i); | |
memset(keyStr, '\0', PASS_PHRASE_MAX); /* done with passphrase */ | |
memset(key, '\0', KEY_MAX); /* and/or key */ | |
fprintf(stderr, "Step 0: Reading %d bytes from %s\n", inTextLength, inFn); | |
/* -------------------------------------------------------------------------- */ | |
if ((int)fread(padA, 1, inTextLength, inFp) < inTextLength) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Input file [%s] read error.\n", inFn); | |
fclose(inFp); | |
fprintf(stderr, "Step 1: Decoding64 raw input %d bytes\n", inTextLength); | |
/* -------------------------------------------------------------------------- */ | |
crc32in = cipherTextLength = ixIn = ixOut = 0; | |
while (puc = nextData64Line(padA, inTextLength, &ixIn)) { | |
/* fprintf(stderr, "%c...%c\n", puc[0], puc[BASE64_ENCODED_LENGTH - 1]);*/ | |
tb_Decode64(puc, BASE64_ENCODED_LENGTH, binLine); /* decode it */ | |
if (ixOut == 0) { /* extract values from first line */ | |
memcpy(&crc32in, binLine, 4); /* first 4 bytes are crc */ | |
if (iBigEndian) tb_ReverseWords32(&crc32in, 1); | |
memcpy(&cipherTextLength, binLine + 4, 4); /* next 4 bytes are count */ | |
if (iBigEndian) tb_ReverseWords32((unsigned int *)&cipherTextLength, 1); | |
memcpy(padB, binLine + 8, BASE64_BIN_LENGTH - 8); /* rest is data */ | |
ixOut = BASE64_BIN_LENGTH - 8; | |
} | |
else { | |
memcpy(padB + ixOut, binLine, BASE64_BIN_LENGTH); | |
ixOut += BASE64_BIN_LENGTH; | |
} | |
} | |
/* First check size for something sensible */ | |
if ((cipherTextLength < CRYPT_BLOCK_CHARS) || (cipherTextLength > (ixOut))) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Bad decode64 length %d (%d - %d)\n", cipherTextLength, 0, ixOut); | |
/* Next check the CRC - this tells us we have picked up all encoded lines. */ | |
crc32check = tb_Crc32(0, padB, cipherTextLength); | |
if (crc32check != crc32in) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Invalid ciphertext CRC - check: %08x in: %08x.\n", crc32check, crc32in); | |
fprintf(stderr, "Step 2: Decrypting %d bytes\n", cipherTextLength); | |
/* ------------------------------------------------------------------- */ | |
memcpy(fr, padB, CRYPT_BLOCK_CHARS); /* get IV */ | |
for (ixIn = CRYPT_BLOCK_CHARS, ixOut = 0; | |
(ixIn + CRYPT_BLOCK_CHARS) <= cipherTextLength; | |
ixIn += CRYPT_BLOCK_CHARS, ixOut += CRYPT_BLOCK_CHARS) { | |
memcpy(cryptBlock, padB + ixIn, CRYPT_BLOCK_CHARS); /* get next block */ | |
if (iBigEndian) tb_ReverseWords32(cryptBlock, CRYPT_BLOCK_INTS); | |
tb_BlowfishDeCrypt(cryptoPad, cryptBlock, prevBlock); /* decrypt */ | |
if (iBigEndian) tb_ReverseWords32(cryptBlock, CRYPT_BLOCK_INTS); | |
if (iBigEndian) tb_ReverseWords32(prevBlock, CRYPT_BLOCK_INTS); | |
prevBlock[0] ^= fr[0]; | |
prevBlock[1] ^= fr[1]; | |
memcpy(fr, cryptBlock, CRYPT_BLOCK_CHARS); | |
memcpy(padB + ixOut, prevBlock, CRYPT_BLOCK_CHARS); | |
} | |
plainTextLength = ixOut; | |
i = cipherTextLength - ixIn; | |
if (i) { /* last block was partial */ | |
plainTextLength = plainTextLength - CRYPT_BLOCK_CHARS + i; | |
} | |
memset(cryptoPad, '\0', TB_BFPAD_SIZE * sizeof(int)); /* done with BF pad */ | |
memset(fr, '\0', CRYPT_BLOCK_CHARS); | |
memset(cryptBlock, '\0', CRYPT_BLOCK_CHARS); | |
memset(prevBlock, '\0', CRYPT_BLOCK_CHARS); | |
fprintf(stderr, "Step 3: Decompressing %d bytes\n", plainTextLength); | |
/* --------------------------------------------------------------------- */ | |
i = plainTextLength - 1; | |
/* fprintf(stderr, "Compress flag %d stored at %d.\n", padB[i], i);*/ | |
compressedTextLength = plainTextLength - 1; | |
if (padB[i]%2) { /* not compressed */ | |
memcpy(padA, padB, compressedTextLength); | |
decompressedTextLength = compressedTextLength; | |
/* fprintf(stderr, " Text was not compressed.\n");*/ | |
} | |
else { /* text was compressed */ | |
/* fprintf(stderr, " About to decompress (%d).\n", compressedTextLength);*/ | |
decompressedTextLength = tb_DecompressText(padB, compressedTextLength, padA, padSize - TB_COMPRESS_TEXT_EXTRA_CHARS); | |
/* fprintf(stderr, " Decompressed to (%d).\n", decompressedTextLength);*/ | |
if (decompressedTextLength == TB_DECOMPRESS_UNABLE) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Unexpected error - decompression. (Key?)\n"); | |
} | |
memset(padB, '\0', padSize); | |
free(padB); | |
fprintf(stderr, "Step 4: Writing %d bytes to %s\n", decompressedTextLength, outFn); | |
/* ----------------------------------------------------------------------------------- */ | |
if ((int)fwrite(padA, 1, decompressedTextLength, outFp) < decompressedTextLength) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Output file [%s] write error.\n", outFn); | |
fclose(outFp); | |
memset(padA, '\0', padSize); | |
free(padA); | |
return(0); | |
} | |
/* ========================================================================== */ | |
static unsigned char *nextData64Line(const unsigned char *workPad, /* input */ | |
int lngth, /* total of the above */ | |
int *ix) { /* next parse char in above */ | |
int n; | |
int ch; | |
int i; | |
/* -------------------------------------------------------------------------- */ | |
n = 0; /* count of consecutive code chars */ | |
i = *ix; | |
/* fprintf(stderr, "parse from %d of %d\n", i, lngth);*/ | |
while (i < lngth) { | |
ch = workPad[i++]; /* next character */ | |
if (((ch >= '0') && (ch <= '9')) /* is it one of the encoding chars? */ | |
|| ((ch >= 'A') && (ch <= 'Z')) | |
|| ((ch >= 'a') && (ch <= 'z')) | |
|| (ch == '{') | |
|| (ch == '}')) { | |
n++; | |
} | |
else if ((ch == ' ') | |
|| (ch == '\r') | |
|| (ch == '\n')) { /* possible line end? */ | |
/* fprintf(stderr, "Terminating char %d i:%d n:%d\n", ch, i, n);*/ | |
/* Do we have an uninterrupted sequence of at least 64 code characters. */ | |
if (n >= BASE64_ENCODED_LENGTH) { | |
*ix = i; | |
return((unsigned char *)(workPad + i - BASE64_ENCODED_LENGTH - 1)); | |
} | |
n = 0; | |
} | |
else { | |
/* fprintf(stderr, "Unexpected char %d i:%d n:%d\n", ch, i, n);*/ | |
n = 0; | |
} | |
} | |
/* Do we have a line with no ending newline character? */ | |
if (n >= BASE64_ENCODED_LENGTH) { | |
*ix = i = lngth; | |
/* fprintf(stderr, "Returning last buffer at %d\n", i - BASE64_ENCODED_LENGTH);*/ | |
return((unsigned char *)(workPad + i - BASE64_ENCODED_LENGTH)); | |
} | |
*ix = lngth; | |
return(NULL); | |
} | |
/* ========================================================================== */ | |
static void usage(char *progname, char *msg) { | |
if (msg) fprintf (stderr, "Error: %s\n", msg); | |
fprintf (stderr, "Usage: %s [options] infile [options] outfile\n", progname); | |
fprintf (stderr, " infile: name of input file\n"); | |
fprintf (stderr, "Options:\n"); | |
fprintf (stderr, " -h to print this usage help and exit\n"); | |
fprintf (stderr, " -k=\"key\" key or passphrase\n"); | |
exit(1); | |
} | |
/* ========================================================================== */ | |
#include "cline.c" | |
#include "errexit.c" | |
#include "endian.c" | |
#include "md5.c" | |
#include "blowfish.c" | |
#include "compr.c" | |
#include "base64.c" | |
#include "crc32.c" | |
#include "rand32.c" | |
#include "randbyte.c" | |
/* ========================================================================== */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* encodex.c - Compress-encrypt-encode64. (Sibling of decodex.c) | |
This program demonstrates the method used by notex to encrypt text. | |
The program is not a complete cryptographic application: in particular, | |
note the absence of any clean-up in case of abnormal termination and | |
a rather unsecure method of key-entry. The purpose of the program is | |
twofold: | |
1: provide a definition and a "bench" for the examination and | |
verification of the ciphertext format and cryptographic code used | |
by the family of programs which share the specification. In order | |
to verify (by comparison) the content of the ciphertext produced | |
by another program, this program will accept a command line string | |
(introduced by the -v=... flag). This string should consist of the | |
(at least) first 20 characters of the ciphertext produced by the | |
other program, fron which the IV will be extracted. This should | |
ensure that all ciphertext (other than non-secret random fill bytes) | |
are matching the ciphetext that is the subject of verification. | |
2: to provide the user with a modicum of an ability to generate | |
ciphertext in this format on platforms which do not yet have a notex | |
equivalent application implemented. (Since this is a simple command-line | |
program, it can be ported to almost any 32-bit platform by simply | |
recompiling it). | |
===================================================================== | |
This software is published with no restrictions on it use or copying, | |
and no guarantee, either explicit or implied. | |
===================================================================== | |
Text encoding can be viewed as a three-step transformation: | |
<a> compression (from [sourceText] to [compressedText]), | |
<b> encryption (from [compressedText] to [cipherext]) and finally | |
<c> base64 encoding (from [ciphertext] to [encoded64Text]). | |
Decoding reverses the order and the "direction" of the above steps. | |
<a> Compression | |
--------------- | |
Compression level is moderate, ensuring only that (if the plaintext | |
consists of natural language words) the volume will be reduced more | |
than the later increase caused by the base64 encoding. It can also | |
make the known-plaintext attacks considerably more difficult. | |
If the volume of text is low or if it has little or no redundancy, | |
the compression step may be omitted (see below, ~i~ trailer). | |
<b> Encryption | |
-------------- | |
Ciphertext is encrypted in 8-byte blocks using Blowfish CBC mode. | |
If the last block of text (before the encryption) is "short", | |
(i.e., it consists of ~n~ bytes, where 1 <= n <= 7), it will be | |
padded (up to 8) with random bytes. If that was the case, a dummy | |
"trailer" of ~n~ random bytes is also added to the ciphertext. This | |
enables the decryption process to discard the padding: if the size | |
of the ciphertext presented to it is not an even multiple of 8 | |
(i.e., there are n bytes over an even multiple of 8), only the | |
first n decrypted bytes of the last full block are included in | |
the plaintext. | |
<c> Base64 Encoding | |
------------------- | |
The final base64 encoding transforms 3 bytes of binary data into 4 | |
characters of ascii text, using only numerals, upper and lower case | |
letters and braces. The text is broken into 64-character wide lines. | |
Each of the three derived forms of text has some additional | |
information added to it (NB.: in the diagram below, relative sizes | |
are preserved, but the scale is not): | |
[----------------------sourceText------------------------] | |
<a> | |
[----------compressedText----------]i | |
<b> | |
---IV--[------------cypherText------------]-pad8- | |
<c> | |
-crc-cnt[--------------encoded64Text--------------]--pad64-- | |
The headers and trailers indicated in the above diagram are: | |
i: single random byte, interpreted as an integer, even if the | |
source text was successfully compressed, odd if it was | |
"uncompressable" and therefore simply copied from | |
[sourceText] to [compressedText]. | |
IV: an 8-byte CBC Initialization Vector. Random but not secret. | |
pad8: optional, padding, as described above. | |
crc: 4-byte crc32 check value of (IV + cipherText + pad8) before | |
base64 encoding. | |
cnt: 4-byte integer byte count of (IV + cipherText + pad8) before | |
base64 encoding. | |
pad64: up to 63 random bytes, padding (crc + cnt + encoded64Text) | |
to a full 64-character (last) line. | |
Note the limit on the text size: 8 Megabytes (8388608 - 1) | |
Programmer: Hrvoje Lukatela, 2002 | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <time.h> | |
#include "toolbox.h" | |
#define KEY_MIN 8 /* minimum key lenght */ | |
#define KEY_MAX 16 /* maximum key length */ | |
#define PASS_PHRASE_MAX 100 /* maximum passphrase length */ | |
#define IV_STR_MIN 24 /* minimum required chars to replicate IV */ | |
#define CRYPT_BLOCK_INTS 2 | |
#define CRYPT_BLOCK_CHARS 8 | |
#define BASE64_BIN_LENGTH 48 | |
#define BASE64_ENCODED_LENGTH 64 | |
#define MAX_FILE_SIZE (8388608 - 1) | |
static void usage(char *, char *); | |
static void dumpX(unsigned char *, int, int); | |
int main(int argc, char **argv) { | |
char inFn[FILENAME_MAX + 2]; | |
FILE *inFp; | |
char outFn[FILENAME_MAX + 2]; | |
FILE *outFp; | |
char ivStr[IV_STR_MIN + 2]; | |
char keyStr[PASS_PHRASE_MAX + 2]; | |
unsigned char key[KEY_MAX]; | |
int ir, padSize; | |
unsigned char randChars[80]; /* pool of random chars for encryption */ | |
int ixRandChars; /* next available to be taken from the pool */ | |
unsigned char *padA; | |
unsigned char *padB; | |
unsigned int cryptoPad[TB_BFPAD_SIZE]; /* Blowfish pad */ | |
int i, n, ixIn, ixOut, iBigEndian, iCrLf; | |
int inTextLength, plainTextLength, plainTextPaddedLength, | |
cipherTextLength, encoded64TextLength; | |
unsigned int crc32, urand; | |
unsigned char *puc; | |
unsigned char uc; | |
unsigned int fr[2]; /* feedback "register" */ | |
unsigned int cryptBlock[2]; /* one we operate on */ | |
unsigned int nextBlock[2]; /* pre-fetched, to make room for encrypted */ | |
unsigned char binLine[BASE64_BIN_LENGTH]; | |
int optch; | |
char *optval; | |
char *progfullname; | |
char *progname; | |
char *pa; | |
/* -------------------------------------------------------------------------- */ | |
progfullname = (char *)malloc(strlen(argv[0]) + 1); | |
strcpy(progfullname, argv[0]); | |
fprintf(stderr, "%s: encrypt text in textec format.\n", progfullname); | |
pa = strrchr(progfullname, TB_SUFFIX_SEPARATOR); | |
if (pa) *pa = '\0'; | |
pa = strrchr(progfullname, TB_DIR_SEPARATOR); | |
if (pa) progname = pa + 1; | |
else progname = progfullname; | |
*keyStr = '\0'; | |
iBigEndian = tb_IsBigEndian(); | |
iCrLf = DEFAULT_TEXT_LINE_ENDING - 1; /* 0: lf, 1: crlf */ | |
*ivStr = '\0'; | |
ir = 0; | |
if (argc < 2) usage(progname, NULL); | |
while ((optch = tb_CLineOption(argc, argv, &optval)) != -1) { | |
switch (optch) { | |
case 'h': case '?': usage(progname, NULL); break; | |
case 'r': ir = 1; break; | |
case 'k': strncpy(keyStr, optval, PASS_PHRASE_MAX); break; | |
case 'v': strncpy(ivStr, optval, IV_STR_MIN); break; | |
default: usage(progname, "unexpected option"); | |
} | |
} | |
if (*keyStr == '\0') tb_ErrorExit(progname, NULL, NULL, __LINE__, "No key given (-k=...).\n"); | |
if (strlen(keyStr) < KEY_MIN) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Key or passphrase (%s) must be at least %d characters long.\n", keyStr, KEY_MIN); | |
if (*ivStr) { | |
if (strlen(ivStr) < IV_STR_MIN) tb_ErrorExit(progname, NULL, NULL, __LINE__, "IV replication string (%s) must be at least %d characters long.\n", ivStr, IV_STR_MIN); | |
} | |
pa = tb_CLineFileName(argc, argv); /* input file name */ | |
if (pa == NULL) tb_ErrorExit(progname, NULL, NULL, __LINE__, "No input file name given\n"); | |
strcpy(inFn, pa); | |
inFp = fopen(inFn, "rb"); | |
if (inFp == NULL) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Input file [%s] open error\n", inFn); | |
fseek(inFp, 0, SEEK_END); | |
inTextLength = ftell(inFp); | |
fseek(inFp, 0, SEEK_SET); | |
if (inTextLength == 0) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Zero-length file [%s]\n", inFn); | |
if (inTextLength > MAX_FILE_SIZE) tb_ErrorExit(progname, NULL, NULL, __LINE__, "File [%s] size greater than limit (%d > %d)\n", inFn, inTextLength, MAX_FILE_SIZE); | |
pa = tb_CLineFileName(argc, argv); /* output file name... */ | |
if (pa == NULL) { | |
strcpy(outFn, "standard output"); | |
outFp = stdout; | |
} | |
else { | |
strcpy(outFn, pa); | |
outFp = fopen(outFn, "wb"); | |
} | |
if (outFp == NULL) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Can't open %s for writing\n", outFn); | |
/* Text will be ping-ponged between two "workpads" - A and B */ | |
padSize = inTextLength + inTextLength / 2 + 1024; | |
padA = (unsigned char *)malloc(padSize + 2); | |
padB = (unsigned char *)malloc(padSize + 2); | |
if ((padA == NULL) || (padB == NULL)) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Memory (%d) allocation error.\n", padSize + padSize); | |
/* Initialize a pool of random characters - low quality but more than | |
adequate for the purpose: they are used only for IV and block padding. | |
*/ | |
urand ^= (unsigned int)time(NULL); /* use whatever was there before */ | |
tb_ReverseWords32(&urand, 1); | |
urand ^= (unsigned int)padB; | |
tb_RandByte(urand); /* initialize random byte source */ | |
tb_RandByte(0); /* waste a byte to offset the integer alignment */ | |
for (i = 0; i < 80; i++) randChars[i] = tb_RandByte(0); /* for padding */ | |
ixRandChars = 0; | |
if (ir) { | |
fprintf(stderr, "Random pad:\n"); | |
dumpX(randChars, 80, 1); | |
} | |
/* Initialize Blowfish pad */ | |
n = strlen(keyStr); /* did we get a key or a passphrase? */ | |
if (n > KEY_MAX) { | |
tb_HashBytes((unsigned char *)keyStr, n, key); | |
n = KEY_MAX; | |
} | |
else memcpy(key, keyStr, n); | |
i = tb_BlowfishInitPad(cryptoPad, key, n); | |
if (i) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Blowfish initialization error %d.\n", i); | |
memset(keyStr, '\0', PASS_PHRASE_MAX); /* done with passphrase */ | |
memset(key, '\0', KEY_MAX); /* and/or key */ | |
fprintf(stderr, "Step 0: Reading %d bytes from %s\n", inTextLength, inFn); | |
/* -------------------------------------------------------------------------- */ | |
if ((int)fread(padA, 1, inTextLength, inFp) < inTextLength) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Input file [%s] read error.\n", inFn); | |
fclose(inFp); | |
fprintf(stderr, "Step 1: Compresing %d bytes ...", inTextLength); | |
/* -------------------------------------------------------------------------- */ | |
uc = (unsigned char)(2 * randChars[ixRandChars++]); /* set flag byte even */ | |
n = tb_CompressText(padA, inTextLength, padB); | |
if (n == TB_COMPRESS_UNABLE) { /* uncompressable text */ | |
fprintf(stderr, " stored!\n"); | |
memcpy(padB, padA, inTextLength); | |
uc = (unsigned char)(uc + 1); /* set flag byte odd */ | |
n = inTextLength; | |
} | |
else fprintf(stderr, " to %d\n", n); | |
/* fprintf(stderr, "Compress flag %d stored at %d.\n", uc, n);*/ | |
padB[n] = uc; | |
plainTextLength = n + 1; | |
/* fprintf(stderr, "Compressed data:\n"); dumpX(padB, compressedTextLength, 3);*/ | |
memset(padA, '\0', padSize); | |
plainTextPaddedLength = plainTextLength; | |
while (plainTextPaddedLength % CRYPT_BLOCK_CHARS) padB[plainTextPaddedLength++] = randChars[ixRandChars++]; | |
/* fprintf(stderr, "Padded plaintext:\n"); dumpX(padB, plainTextPaddedLength, 3);*/ | |
/* Load feedback register with Initialization Vector (random bytes) */ | |
if (*ivStr) { /* replicate Initialization Vector */ | |
tb_Decode64((unsigned char *)ivStr, IV_STR_MIN, binLine); | |
memcpy(fr, binLine + 8, CRYPT_BLOCK_CHARS); | |
} | |
else { | |
for (i = 0, puc = (unsigned char *)fr; i < CRYPT_BLOCK_CHARS; i++, puc++) *puc = randChars[ixRandChars++]; | |
} | |
fprintf(stderr, "Step 2: Encrypting %d bytes, IV: %8x %8x\n", plainTextLength, fr[0], fr[1]); | |
/* -------------------------------------------------------------------------- */ | |
memcpy(cryptBlock, padB, CRYPT_BLOCK_CHARS); /* retrieve first block of plaintext */ | |
memcpy(nextBlock, padB + CRYPT_BLOCK_CHARS, CRYPT_BLOCK_CHARS); /* pre-fetch block of plaintext */ | |
memcpy(padB, fr, CRYPT_BLOCK_CHARS); /* write IV as first block of ciphertext */ | |
cipherTextLength = CRYPT_BLOCK_CHARS; /* count written bytes */ | |
for (i = CRYPT_BLOCK_CHARS; | |
i < plainTextPaddedLength; | |
i += CRYPT_BLOCK_CHARS) { | |
cryptBlock[0] ^= fr[0]; cryptBlock[1] ^= fr[1]; /* chain... */ | |
if (iBigEndian) tb_ReverseWords32(cryptBlock, CRYPT_BLOCK_INTS); | |
tb_BlowfishEnCrypt(cryptoPad, cryptBlock, cryptBlock); | |
if (iBigEndian) tb_ReverseWords32(cryptBlock, CRYPT_BLOCK_INTS); | |
memcpy(fr, cryptBlock, CRYPT_BLOCK_CHARS); /* save feedback register */ | |
memcpy(padB + i, cryptBlock, CRYPT_BLOCK_CHARS); /* write encrypted data */ | |
cipherTextLength += CRYPT_BLOCK_CHARS; /* count written bytes */ | |
memcpy(cryptBlock, nextBlock, CRYPT_BLOCK_CHARS); /* load next block of data */ | |
memcpy(nextBlock, padB + i + CRYPT_BLOCK_CHARS, CRYPT_BLOCK_CHARS); /* pre-fetch next block */ | |
} | |
cryptBlock[0] ^= fr[0]; cryptBlock[1] ^= fr[1]; /* process last block */ | |
if (iBigEndian) tb_ReverseWords32(cryptBlock, CRYPT_BLOCK_INTS); | |
tb_BlowfishEnCrypt(cryptoPad, cryptBlock, cryptBlock); /* encrypt */ | |
if (iBigEndian) tb_ReverseWords32(cryptBlock, CRYPT_BLOCK_INTS); | |
memcpy(padB + i, cryptBlock, CRYPT_BLOCK_CHARS); /* write encrypted data */ | |
cipherTextLength += CRYPT_BLOCK_CHARS; /* count written bytes */ | |
if (plainTextPaddedLength != plainTextLength) { /* was it padded? */ | |
i = CRYPT_BLOCK_CHARS + plainTextLength - plainTextPaddedLength; | |
/* fprintf(stderr, "partial block signal count %d\n", i);*/ | |
while (i) { | |
/* If we had to pad, only a first few bytes of the last block are valid | |
data - rest of the last block is random junk. We will signal this by | |
appending the same number (as the number of valid data bytes) to the | |
ciphertext. When decoding, a "short" last block is a signal that only | |
this many bytes of last full block are data. */ | |
padB[cipherTextLength++] = randChars[ixRandChars++]; | |
i--; | |
} | |
} | |
memset(cryptoPad, '\0', TB_BFPAD_SIZE * sizeof(int)); /* done with Blowfish pad */ | |
memset(fr, '\0', CRYPT_BLOCK_CHARS); | |
memset(cryptBlock, '\0', CRYPT_BLOCK_CHARS); | |
memset(nextBlock, '\0', CRYPT_BLOCK_CHARS); | |
fprintf(stderr, "Step 3: Encoding64 %d bytes\n", cipherTextLength); | |
/* ------------------------------------------------------------------- */ | |
crc32 = tb_Crc32(0, padB, cipherTextLength); | |
/* fprintf(stderr, " Crs32 of %d bytes: %08x\n", cipherTextLength, crc32);*/ | |
if (iBigEndian) tb_ReverseWords32(&crc32, 1); | |
memcpy(binLine, &crc32, 4); /* first 4 bytes: crc */ | |
n = cipherTextLength; | |
if (iBigEndian) tb_ReverseWords32((unsigned int *)&n, 1); | |
memcpy(binLine + 4, &n, 4); /* next 4 bytes: count */ | |
i = 8; /* next free element in the composition line */ | |
ixIn = ixOut = 0; | |
while (ixIn < cipherTextLength) { /* until all input bytes are used */ | |
binLine[i++] = padB[ixIn++]; /* get next byte of ciphertext */ | |
if (i == BASE64_BIN_LENGTH) { /* line is full */ | |
tb_Encode64(binLine, BASE64_BIN_LENGTH, padA + ixOut); /* encode it */ | |
ixOut += BASE64_ENCODED_LENGTH; /* 64 = 48 * 4 / 3 */ | |
if (iCrLf) padA[ixOut++] = '\r'; | |
padA[ixOut++] = '\n'; | |
i = 0; | |
} | |
} | |
if (i) { /* last partially filled line - pad, encode, output */ | |
while (i < BASE64_BIN_LENGTH) binLine[i++] = randChars[ixRandChars++]; | |
tb_Encode64(binLine, BASE64_BIN_LENGTH, padA + ixOut); | |
ixOut += BASE64_ENCODED_LENGTH; | |
if (iCrLf) padA[ixOut++] = '\r'; | |
padA[ixOut++] = '\n'; | |
} | |
encoded64TextLength = ixOut; | |
memset(binLine, '\0', BASE64_BIN_LENGTH); | |
memset(padB, '\0', padSize); | |
free(padB); | |
fprintf(stderr, "Step 4: Writing %d bytes to %s\n", encoded64TextLength, outFn); | |
if ((int)fwrite(padA, 1, encoded64TextLength, outFp) < encoded64TextLength) tb_ErrorExit(progname, NULL, NULL, __LINE__, "Output file [%s] write error.\n", outFn); | |
fclose(outFp); | |
memset(padA, '\0', padSize); | |
free(padA); | |
return(0); | |
} | |
/* ========================================================================== */ | |
static void dumpX(unsigned char *pad, /* dump what */ | |
int n, /* dump how much */ | |
int ind) { /* 1: hex, 2:text, 3:both */ | |
int i, j; | |
char outLineC[64 + 1]; | |
char outLineX[64 + 1]; | |
unsigned char uc; | |
/* -------------------------------------------------------------------------- */ | |
memset(outLineC, ' ', 64); | |
memset(outLineX, ' ', 64); | |
outLineC[60] = outLineX[60] = '\0'; | |
j = 0; | |
for (i = 0; i < n; i++) { | |
uc = pad[i]; | |
if (ind & 1) sprintf(outLineX + j, "%02x", uc); | |
if ((uc < 20) || (uc > 126)) uc = 22; | |
if (ind & 1) sprintf(outLineC + j, " %c", uc); | |
j += 2; | |
if (j >= 64) { | |
if (ind & 1) fprintf (stderr, "[%s]\n", outLineX); | |
if (ind & 2) fprintf (stderr, "(%s)\n", outLineC); | |
memset(outLineC, ' ', 64); | |
memset(outLineX, ' ', 64); | |
j = 0; | |
} | |
} | |
if (j) { | |
if (ind & 1) fprintf (stderr, "[%s]\n", outLineX); | |
if (ind & 2) fprintf (stderr, "(%s)\n", outLineC); | |
} | |
return; | |
} | |
/* ========================================================================== */ | |
static void usage(char *progname, char *msg) { | |
if (msg) fprintf (stderr, "Error: %s\n", msg); | |
fprintf (stderr, "Usage: %s [options] infile [options] outfile\n", progname); | |
fprintf (stderr, " infile: name of input file\n"); | |
fprintf (stderr, "Options:\n"); | |
fprintf (stderr, " -h to print this usage help and exit\n"); | |
fprintf (stderr, " -k=\"key\" key or passphrase\n"); | |
fprintf (stderr, " -v=abc... to replicate I.V.\n"); | |
fprintf (stderr, " -r to report random pad\n"); | |
exit(1); | |
} | |
/* ========================================================================== */ | |
#include "base64.c" | |
#include "blowfish.c" | |
#include "cline.c" | |
#include "compr.c" | |
#include "crc32.c" | |
#include "endian.c" | |
#include "errexit.c" | |
#include "md5.c" | |
#include "rand32.c" | |
#include "randbyte.c" | |
/* ========================================================================== */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment