#!/usr/bin/perl -w use Fcntl qw(:seek); use constant READ_LENGTH => 2 ** 13; sub pad($$) { my @c1 = unpack("W*", shift); my @c2 = unpack("W*", shift); pack("W*", map { ($c1[$_] & ~$c2[$_]) | (~$c1[$_] & $c2[$_]); } (0 .. $#c1)) } for my $f (@ARGV) { eval { die $! unless -e $f; unless (-e "$f.pad") { # generate one-time pad die $! unless -f $f and -r _; my $n = (stat $f)[7] or die $!; open PAD, ">", "$f.pad" or die $!; open RND, "<", "/dev/urandom" or die $!; while ($n) { my $buf; my $c = read(RND, $buf, READ_LENGTH > $n ? $n : READ_LENGTH); if ($c) { $n -= $c; print PAD $buf; } elsif (defined($c)) { next; } else { die $! } } close PAD or die $!; close RND or die $!; } die $! unless -f "$f.pad"; if ((stat "$f.pad")[7] == (stat $f)[7]) { # encrypt/decrypt open PAD, "<", "$f.pad" or die $!; open DATA, "+<", $f or die $!; my ($n, $buf, $pad); while ($n = read(DATA, $buf, READ_LENGTH)) { seek(DATA, -$n, SEEK_CUR) or die $!; while (length $pad < length $buf) { defined(read(PAD, $pad, READ_LENGTH, length $pad)) or die $!; } my $padded = pad($buf, substr($pad, 0, length $buf, "")); print DATA $padded or die $!; } warn "read error: $!\n" if $n < 0; close DATA or die $!; close PAD or die $!; } else { warn "data / pad size mismatch. delete pad.\n"; die $!; } }; warn "error processing file '$f': $@" if $@; }