#!/usr/local/bin/perl ###################################################################### # Shredder.pm # A data protection tool ###################################################################### package Shredder; use strict; use warnings; sub new { my $class = shift; my $config = shift; my $self = {}; bless $self, $class; return $self; } sub Shred { my $self = shift; my $inval = shift; my $len = length ($inval); my $fmt = "b*"; my @bits; my @tmpbits; my @return; @bits = split //, unpack $fmt, $inval; my $i = 0; my $offset = 0; foreach my $bit (@bits) { push @{$tmpbits[$i]}, $bit; $i++; if ($i == 8) { $i = 0; } if ($i == $offset) { $offset ++; if ($offset == 8) { $offset = 0; } $i = $offset; } } $i = 0; $len = scalar (@{$tmpbits[0]}); $fmt = "LLb*"; foreach my $arBits (@tmpbits) { my $chk = join '', @$arBits; if (length($chk)%8) { my $pad = 8 - (length($chk) % 8); $chk .= '0' x $pad; } $return[$i] = pack $fmt, $i, $len, $chk; $i++; } return @return; } sub Tape { my $self = shift; my @invals = @_; my $fmt = "LLb*"; my $returnstr = ""; my @tmpBits; my $len = 0; foreach my $val (@invals) { my ($fileno, $tlen, $bits) = unpack $fmt, $val; my @arbits = split //, $bits; if (!$len) { $len = $tlen; } if ($len != $tlen) { die "Length reported in $fileno doesn't match prior reports!\n"; } if ($tmpBits[$fileno]) { die "You've already given me a $fileno value!\n"; } @{$tmpBits[$fileno]} = @arbits; } my $offset = 0; for (my $i = 0; $i < $len; $i ++) { my $b = $offset; for (my $n = 0; $n < 8; $n ++) { $returnstr .= $tmpBits[$b][$i]; $b++; if ($b == 8) { $b = 0; } } $offset ++; if ($offset == 8) { $offset = 0; } } $returnstr = pack "b*", $returnstr; return $returnstr; } sub ShredFile { my ($self, $testfile) = @_; my $tststr = ""; open (IN, "<$testfile"); binmode IN; my $mode = ">"; my $processed = 0; while (read (IN, $tststr, 102400)) { my $written; my @outs = $self->Shred($tststr); for (my $i = 0; $i < 8; $i ++) { open (OUT, "$mode$testfile.$i"); binmode OUT; $written += syswrite OUT, $outs[$i], length($outs[$i]); close OUT; $mode = ">>"; } $processed += $written; print "$processed bytes shredded...\n"; } print "Done!\n"; } sub TapeFile { my ($self, $testfile) = @_; my @reads; my @fh; my $mode = ">"; my $processed = 0; for (my $i = 0; $i < 8; $i ++) { open ($fh[$i], "<$testfile.$i") || die "OUCH: $testfile.$i: $!\n"; binmode $fh[$i]; } open (OUT, ">$testfile.out"); binmode OUT; while (read ($fh[0], $reads[0], 12808)) { my $written = 0; for (my $i = 1; $i < 8; $i ++) { read ($fh[$i], $reads[$i], 12808); } my $outval = $self->Tape(@reads); $written += syswrite OUT, $outval, length($outval); $processed += $written; print "$processed bytes taped...\n"; } close OUT; print "Done!\n"; for (my $i = 1; $i < 8; $i ++) { close $fh[$i]; } } =pod =head1 NAME Shredder =head1 SYNOPSIS use Shredder; my $s = new Shredder; $s->ShredFile("filename.txt"); $s->TapeFile("filename.txt"); =head1 DESCRIPTION Shredder is a novel concept in data protection tools. While Shredder IS NOT ENCRYPTION (doesn't claim to be, never has, never will), it is intended to be used concurrently with encryption tools to build a stronger, more airtight data protection scheme. Shredder takes a file and "shreds" it down to 8 files, each roughly one eighth the size of the original. These shreds are built by taking each byte of the source file and splitting it bitwise out to the eight different files. (Padding out with 0 bits where needed to make a full byte.) These shredstrings, along with a couple of control characters, are then written out to eight different files. Since (particularly with ASCII text files) some bits are used less frequently then others, the bits are round robined between the files, for example: SOURCE: 0110011011100110 bit 1, byte 1 -->F0 00 F1 11<-- bit 1, byte 2 F2 11 F3 01 F4 00 F5 10 F6 11 F7 01 And so on. Why is this useful? If one distributes these shreds out to seperate repositories, no one will be able to reconstitute the original file without having collected enough of the shred files to "guess" the missing bits. The attacker's problem will be compounded if one encrypts the source file, and then encrypts the shreds. If the source file and shreds are cleartext, of course, the attacker's problem is simple: Collect the files, and reconstitute (or Tape) them back together (possibly inserting "guess" bits based on knowledge of the data in place of shred files.) If the source file is encrypted but not shredded, the attacker "simply" has to break the encryption. By providing a means of splitting the file at a "subatomic" level, I intend to increase the effectiveness of standard data protection techniques. =head1 USAGE Simply call Shredfile and Tapefile with a filename to disintegrate or reconstitute a file. Shredfile creates shred files named filename.0 - filename.8; Tapefile assumes the existence of filename.0 - filename.8 and creates filename.out (for safety.) One can also pass a string to Shred and get back an array of eight strings, which when passed back to Tape will reconstitute the original string. Thus, you can build your own Shred and Tape tools. =head1 TODO Make the process quicker. It's abyssmally slow right now for large files. Probably, this could be improved by rewriting portions in C, but I haven't had time yet. =head1 AUTHOR Jason Klueber Copyright 2004 This program is free software. You can modify or distribute it under the same conditions as Perl itself. =cut 1;