A few days ago I resurrected all my files from a desktop system I haven’t used for almost six years. Below is my implementation of secret splitting as described in Bruce Schneier’s book “Applied Cryptography, 2nd edition”.

The idea is basically: generate a random keystream, xor the plaintext with the random keystream, use separate couriers and send the random keystream and the ciphertext to the final destination using separate routes, and finally combine the random keystream with the ciphertext to recreate the plaintext. None of the couriers know which piece they are carrying.

To improve this idea would be to generate more than one random keystream, xor the keystreams and the plaintext, thus requiring more couriers and different routes for each datastream.

And, by the way, xor crypto isn’t really that strong.

/*
 secretsplitting.c -- Implementation of Secret Splitting.

 See page 70 of Applied Cryptography, 2nd edition by Bruce Schneier.

 Copyright (C) 2005 Trond Endrestøl <Trond.Endrestol@ximalas.info>

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*/
#include <sys/stat.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

const char *progname;

void help(int status);

void version(void);

void split(const char *plaintextfilename,
           const char *keystreamfilename,
           const char *cryptotextfilename);

void merge(const char *cryptotextfilename,
           const char *keystreamfilename,
           const char *plaintextfilename);

FILE *Fopen(const char *path, const char *mode);

int main(int argc, char **argv)
{
  int splitting = 1, c;

  progname = argv[0];

  opterr = 0;
  while ( (c = getopt(argc, argv, "smhv")) != -1) {
    switch (c) {
      case 's':
        splitting = 1;
        break;

      case 'm':
        splitting = 0;
        break;

      case 'h':
        help(EXIT_SUCCESS);
        break;

      case 'v':
        version();
        break;

      default:
        fprintf(stderr, "%s: unknown option -%c\n", progname, optopt);
        break;
    } /* switch */
  } /* while */

  if (argc - optind > 2) {
    umask(S_IRWXG | S_IRWXO);

    if (splitting) {
      split(argv[optind], argv[optind + 1], argv[optind + 2]);
    } /* if */
    else {
      merge(argv[optind], argv[optind + 1], argv[optind + 2]);
    } /* else */
  } /* if */
  else {
    help(EXIT_FAILURE);
  } /* else */

  return EXIT_SUCCESS;
} /* main() */

void help(int status)
{
  fprintf((status == EXIT_SUCCESS) ? stdout : stderr,
          "Usage: %s -s <plaintext> <keystrem> <cryptotext>  (default)\n"
          "   or: %s -m <keystream> <cryptotext> <plaintext>\n"
          "   or: %s -h\n"
          "   or: %s -v\n\n"

          "Options:\n"
          "  -s  split  (this is default)\n"
          "        plaintext is input, keystream and cryptotext are output\n"
          "  -m  merge\n"
          "        keystream and cryptotext are input, plaintext is output\n"
          "  -h  show this help\n"
          "  -v  show version\n",
          progname, progname, progname, progname);

  exit(status);
} /* help() */

void version(void)
{
  printf("%s version 1.0\n\n"

         "Copyright (C) 2005 Trond Endrestøl <Trond.Endrestol@ximalas.info>\n\n"

         "This program is free software; you can redistribute it and/or modify\n"
         "it under the terms of the GNU General Public License as published by\n"
         "the Free Software Foundation; either version 2 of the License, or\n"
         "(at your option) any later version.\n\n"

         "This program is distributed in the hope that it will be useful,\n"
         "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
         "GNU General Public License for more details.\n\n"

         "You should have received a copy of the GNU General Public License\n"
         "along with this program; if not, write to the Free Software\n"
         "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.\n\n"

         "Send bug reports to Trond Endrestøl <Trond.Endrestol@ximalas.info>\n",
         progname);

  exit(EXIT_SUCCESS);
} /* version() */

void split(const char *plaintextfilename,
           const char *keystreamfilename,
           const char *cryptotextfilename)
{
  FILE *plaintext, *keystream, *cryptotext;
  int m;
  char r, s;

  plaintext  = Fopen(plaintextfilename,  "rb");
  keystream  = Fopen(keystreamfilename,  "wb");
  cryptotext = Fopen(cryptotextfilename, "wb");

  srandomdev();

  while (1) {
    if (ferror(plaintext)) {
      fprintf(stderr, "%s: file error occurred on %s\n",
              progname, plaintextfilename);
      exit(EXIT_FAILURE);
    } /* if */

    if ( (m = fgetc(plaintext)) == EOF) {
      break;
    } /* if */

    r = random();
    s = m ^ r;

    if (fputc(r, keystream) == EOF) {
      fprintf(stderr, "%s: fputc(r, %s): %s (%d)\n",
              progname, keystreamfilename, strerror(errno), errno);
      exit(EXIT_FAILURE);
    } /* if */

    if (fputc(s, cryptotext) == EOF) {
      fprintf(stderr, "%s: fputc(s, %s): %s (%d)\n",
              progname, cryptotextfilename, strerror(errno), errno);
      exit(EXIT_FAILURE);
    } /* if */
  } /* while */
} /* split() */

void merge(const char *cryptotextfilename,
           const char *keystreamfilename,
           const char *plaintextfilename)
{
  FILE *keystream, *cryptotext, *plaintext;
  char r, s, m;

  keystream  = Fopen(keystreamfilename,  "rb");
  cryptotext = Fopen(cryptotextfilename, "rb");
  plaintext  = Fopen(plaintextfilename,  "wb");

  while (1) {
    r = fgetc(keystream);
    s = fgetc(cryptotext);

    if (feof(keystream) || feof(cryptotext)) {
      break;
    } /* if */

    if (ferror(keystream)) {
      fprintf(stderr, "%s: file error occurred on %s\n",
              progname, keystreamfilename);
      exit(EXIT_FAILURE);
    } /* if */

    if (ferror(cryptotext)) {
      fprintf(stderr, "%s: file error occurred on %s\n",
              progname, cryptotextfilename);
      exit(EXIT_FAILURE);
    } /* if */

    m = r ^ s;

    if (fputc(m, plaintext) == EOF) {
      fprintf(stderr, "%s: fputc(m, %s): %s (%d)\n",
              progname, plaintextfilename, strerror(errno), errno);
      exit(EXIT_FAILURE);
    } /* if */
  } /* while */

  if (feof(keystream) && !feof(cryptotext)) {
    fprintf(stderr, "%s: contents of %s ended prematurely\n",
            progname, keystreamfilename);
    exit(EXIT_FAILURE);
  } /* if */

  if (!feof(keystream) && feof(cryptotext)) {
    fprintf(stderr, "%s: contents of %s ended prematurely\n",
            progname, cryptotextfilename);
    exit(EXIT_FAILURE);
  } /* if */
} /* merge() */

FILE *Fopen(const char *path, const char *mode)
{
  FILE *ret;

  if ( (ret = fopen(path, mode)) == NULL) {
    fprintf(stderr, "%s: fopen(%s, %s): %s (%d)\n",
            progname, path, mode, strerror(errno), errno);
    exit(EXIT_FAILURE);
  } /* if */

  return ret;
} /* Fopen() */

/* secretsplitting.c */