/* FILE : mmpc_snd.c
** AUTHOR : Jim Shapiro
** DATE : 19 March, 1999
** DISCLAIMER : No liability is assumed by the author for any use
** made of this program.
** DISTRIBUTION: Any use may be made of this program, as long as the
** clear acknowledgment is made to the author in code
** and runtime executables. The author retains
** copyright.
*/
#define THIS_IS_MAIN
#include <stdio.h>
#include <stdlib.h>
#include <strings.h> /* bzero(), bcopy() */
#include <sys/stat.h>
#include <time.h>
#include "msg_dgst_rsa.h"
#include "blowfish.h"
#include "package.h" /* must come after blowfish.h */
#include "mmpc_snd.h" /* must come after blowfish.h */
#include "clcg_rand.h"
/* (m)ulti-(m)essage (p)ackage (c)haffing - sender */
/* MD5 (the default) or SHA */
#define SHA
#ifndef SHA
const int Digest_bytes = 16;
const int Block_bytes = 24;
#else
const int Digest_bytes = 20;
const int Block_bytes = 28;
#endif
int main(int argc, char **argv)
{
char buf[101];
unsigned char chaff_p[Block_bytes];
unsigned char in_block_p[8];
unsigned char pack_block_p[8];
unsigned char digest_p[Digest_bytes];
int file_count;
int rv = EXIT_FAILURE;
unsigned long i;
unsigned long j;
unsigned long chaff_blocks;
unsigned long total_blocks = 0lu;
unsigned long n;
unsigned long *block_indices_p;
unsigned long *ran_array_p;
FILE *cipher_p;
FILE_INFO *fi_p;
FILE_INFO *fi_r;
do {
if((argc == 2) && (strcmp(argv[1], "-h") == 0)) {
help(argv[0]);
break;
}
#ifndef SHA
(void)fputs("Using hmac_md5 for digest.\n", stderr);
#else
(void)fputs("Using hmac_sha for digest.\n", stderr);
#endif
(void)printf("How many files do you want to package-chaff ? ");
file_count = atoi(fgets(buf, sizeof buf, stdin));
if((fi_p = (FILE_INFO *)malloc(file_count * sizeof(FILE_INFO)))
== NULL) {
(void)fprintf(stderr,
"Cannot malloc %d FILE_INFO structure%s!...\n",
file_count, file_count == 1 ? "" : "s");
break;
}
/* set (internal) public arrays */
package_one_time_set(time(NULL), 123456);
for(i = 0; i < file_count; i++) {
total_blocks += file_info(i, fi_p + i);
}
(void)printf("How many chaff blocks do you want to add ? ");
if((chaff_blocks = atol(fgets(buf, sizeof buf, stdin))) <
Min_chaff_blocks) {
(void)printf(
"Increasing number of chaff blocks to %lu for security.\n",
chaff_blocks = Min_chaff_blocks);
}
/* n starts = total_blocks, but gets decremented by get_ran() */
n = total_blocks += chaff_blocks;
(void)printf("Total blocks = %lu\n", total_blocks);
(void)printf("Enter name of ciphertext file : ");
(void)fgets(buf, sizeof buf, stdin);
buf[strlen(buf) - 1] = '\0';
if((cipher_p = fopen(buf, "wb")) == NULL) {
(void)printf("Cannot open chaff file \"%s\"!...\n", buf);
break;
}
Endian = check_byte_order();
if((ran_array_p = (unsigned long *)malloc(total_blocks *
sizeof(unsigned long))) == NULL) {
(void)printf(
"Cannot malloc ran_array_p of %lu element%s!...\n",
total_blocks, total_blocks == 1lu ? "" : "s");
break;
}
if((block_indices_p = (unsigned long *)malloc(total_blocks *
sizeof(unsigned long))) == NULL) {
(void)printf(
"Cannot malloc block_indices_p of %lu element%s!...\n",
total_blocks, total_blocks == 1lu ? "" : "s");
break;
}
/* Load array for random data indices. */
for(i = 0; i < total_blocks; i++) {
ran_array_p[i] = i;
block_indices_p[i] = file_count; /* flag for chaff */
}
for(i = 0, fi_r = fi_p; i < file_count; i++, fi_r++) {
for(j = 0; j < fi_r->data_blocks; j++) {
block_indices_p[get_ran(ran_array_p, &n)] = i;
}
}
/* Done with array -- rans are in block_indices_p. */
free(ran_array_p);
for(i = 0; i < total_blocks; i++) {
(void)sprintf(buf, "%lu", i);
if((j = block_indices_p[i]) != file_count) { /* data block */
if(++(fi_p[j].i) < fi_p[j].data_blocks - 1) {
/* >= 2 blocks to go */
if(fread(in_block_p, sizeof(unsigned char), 8,
fi_p[j].plain_p) != 8) {
(void)printf("Read error on file \"%s\"!...\n",
fi_p[j].name_p);
exit(EXIT_FAILURE);
}
package_transform(fi_p[j].ms_p, &(fi_p[j].pack_array),
fi_p[j].i, in_block_p, pack_block_p);
if(fwrite(pack_block_p, sizeof(unsigned char), 8,
cipher_p) != 8) {
(void)puts("Could not write data to chaff file!...");
exit(EXIT_FAILURE);
}
#ifndef SHA
hmac_md5_string(pack_block_p, 8, fi_p[j].key_p,
fi_p[j].key_len, digest_p);
#else
hmac_sha_string(pack_block_p, 8, fi_p[j].key_p,
fi_p[j].key_len, digest_p);
#endif
if(fwrite(digest_p, sizeof(unsigned char), Digest_bytes,
cipher_p) != Digest_bytes) {
(void)puts("Could not write HMAC to chaff file!...");
exit(EXIT_FAILURE);
}
} else if((fi_p[j].i) == fi_p[j].data_blocks - 1) {
/* partial? */
if(fi_p[j].bytes_leftover == 0) { /* create block */
/* bzero(in_block_p, 7); 0 pad, if desired */
in_block_p[7] = 8; /* sentinel count */
} else { /* partial block */
int extra = fi_p[j].bytes_leftover;
if(fread(in_block_p, sizeof(unsigned char), extra,
fi_p[j].plain_p) != extra) {
(void)printf("Read error on file \"%s\"!...\n",
fi_p[j].name_p);
exit(EXIT_FAILURE);
}
/*
if(extra != 7) { 0 pad, if desired
bzero(in_block_p + extra, 7 - extra);
}
*/
in_block_p[7] = 8 - extra; /* sentinel count */
}
package_transform(fi_p[j].ms_p, &(fi_p[j].pack_array),
fi_p[j].i, in_block_p, pack_block_p);
if(fwrite(pack_block_p, sizeof(unsigned char), 8,
cipher_p) != 8) {
(void)puts("Could not write data to chaff file!...");
exit(EXIT_FAILURE);
}
#ifndef SHA
hmac_md5_string(pack_block_p, 8, fi_p[j].key_p,
fi_p[j].key_len, digest_p);
#else
hmac_sha_string(pack_block_p, 8, fi_p[j].key_p,
fi_p[j].key_len, digest_p);
#endif
if(fwrite(digest_p, sizeof(unsigned char), Digest_bytes,
cipher_p) != Digest_bytes) {
(void)puts("Could not write HMAC to chaff file!...");
exit(EXIT_FAILURE);
}
} else { /* last pseudo-block */
if(fwrite(fi_p[j].ms_p, sizeof(unsigned char), 8,
cipher_p) != 8) {
(void)puts("Could not write data to chaff file!...");
exit(EXIT_FAILURE);
}
#ifndef SHA
hmac_md5_string(fi_p[j].ms_p, 8, fi_p[j].key_p,
fi_p[j].key_len, digest_p);
#else
hmac_sha_string(fi_p[j].ms_p, 8, fi_p[j].key_p,
fi_p[j].key_len, digest_p);
#endif
if(fwrite(digest_p, sizeof(unsigned char), Digest_bytes,
cipher_p) != Digest_bytes) {
(void)puts("Could not write HMAC to chaff file!...");
exit(EXIT_FAILURE);
}
}
} else { /* chaff block */
for(j = 0; j < Block_bytes; j++) {
chaff_p[j] = 256 * combined_lcg();
}
if(fwrite(chaff_p, sizeof(unsigned char), Block_bytes,
cipher_p) != Block_bytes) {
(void)puts("Could not write chaff to file!...");
exit(EXIT_FAILURE);
}
}
}
for(i = 0; i < file_count; i++) {
(void)fclose(fi_p[i].plain_p);
}
free(fi_p);
free(block_indices_p);
(void)fclose(cipher_p);
rv = EXIT_SUCCESS;
} while(0);
return rv;
}
static unsigned long file_info(int index, FILE_INFO *fi_p)
{
char buf[101];
char byte_s[3] = { '\0', '\0', '\0' };
int i;
int j;
int len;
unsigned long data_bytes;
unsigned long data_blocks;
struct stat stat_struct;
(void)printf("Enter name of file %d: ",
index + 1);
(void)fgets(buf, sizeof buf, stdin);
buf[strlen(buf) - 1] = '\0';
if((fi_p->plain_p = fopen(buf, "rb")) == NULL) {
(void)printf("Cannot open plaintext file \"%s\"!...\n", buf);
exit(EXIT_FAILURE);
}
if(stat(buf, &stat_struct) == -1) {
(void)printf("Cannot stat \"%s\"!...\n", buf);
exit(EXIT_FAILURE);
}
fi_p->name_p = strdup(buf);
fi_p->data_bytes = data_bytes = stat_struct.st_size;
/* possible extra sentinel block and always 1 extra for key */
fi_p->data_blocks = data_blocks =
(data_bytes + 16) / BYTES_PER_BLOCK;
fi_p->bytes_leftover = data_bytes % BYTES_PER_BLOCK;
(void)printf("Data file \"%s\" (%lu bytes) requires %lu (%d) "
"byte data blocks.\n",
buf, data_bytes, data_blocks, BYTES_PER_BLOCK);
(void)printf("Enter key (in hexadecimal) for %-15s: ",
fi_p->name_p);
(void)fgets(buf, sizeof buf, stdin);
len = strlen(buf);
buf[--len] = '\0';
if((len % 2) != 0) {
(void)puts(
"Sorry, you did not enter an even number of characters!...");
exit(EXIT_FAILURE);
}
fi_p->key_len = len >> 1; /* convert from hex to byte count */
if((fi_p->key_p = (unsigned char *)malloc(fi_p->key_len *
sizeof(unsigned char))) == NULL) {
(void)fprintf(stderr,
"Could not malloc %d bytes for key!...\n", len);
exit(EXIT_FAILURE);
}
for(i = j = 0; i < len; i += 2) {
bcopy(buf + i, byte_s, 2);
fi_p->key_p[j++] = (unsigned char)strtol(byte_s, (char **)NULL,
16);
}
fi_p->i = 0; /* starting index (-1) for package transform */
package_initialize(&(fi_p->pack_array), fi_p->ms_p);
return data_blocks;
}
static unsigned long get_ran(unsigned long *a_p, unsigned long *n_p)
{
unsigned long i = (*n_p * combined_lcg());
unsigned long rv = a_p[i];
a_p[i] = a_p[--*n_p];
return rv;
}
static void help(char *program)
{
(void)printf(
"\n\"%s\" will intermix any number of files, storing them\n"
"package-transformed as 8 byte data plus either 16 byte MD5\n"
"or 20 byte SHA-1 HMAC pairs. For each file a key is entered\n"
"as a string of hexadecimal characters. If the user chooses,\n"
"any number of pseudo-random chaff blocks can be inter-\n"
"spersed into the data blocks. The companion program\n"
"\"mmpc_rec\" is used for decryption.\n\n",
program);
}