Skip to content

Instantly share code, notes, and snippets.

Created February 9, 2015 17:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/1d53f1a2599c2c2480ac to your computer and use it in GitHub Desktop.
Save anonymous/1d53f1a2599c2c2480ac to your computer and use it in GitHub Desktop.
/* 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"
/* ========================================================================== */
/* 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