| 1 | #! /usr/bin/perl -w |
| 2 | |
| 3 | use Catacomb; |
| 4 | |
| 5 | my $GRIPE = 0; |
| 6 | my $DEBUG = 0; |
| 7 | my $QUIS = $0; $QUIS =~ s:^.*/::; |
| 8 | |
| 9 | my $MODE = \&encrypt; |
| 10 | my $OFILE = "-"; |
| 11 | my $TAG = "ciphersaber-%s"; |
| 12 | |
| 13 | sub usage { |
| 14 | my $f = shift; |
| 15 | print $f "Usage: $QUIS [-de] [-t TAG] [-o FILE] file...\n"; |
| 16 | } |
| 17 | sub version { |
| 18 | my $f = shift; |
| 19 | print $f "$QUIS, catacomb-perl version $Catacomb::VERSION\n"; |
| 20 | } |
| 21 | sub help { |
| 22 | my $f = shift; |
| 23 | version($f); |
| 24 | print $f "\n"; |
| 25 | usage($f); |
| 26 | print $f <<EOF; |
| 27 | |
| 28 | Implements the CipherSaber file encryption system (as described in |
| 29 | http://ciphersaber.gurus.com/). Options available are: |
| 30 | |
| 31 | -h Display this help text. |
| 32 | -v Show the program's version number. |
| 33 | -u Show this usage message. |
| 34 | |
| 35 | -d Decrypt the input files. |
| 36 | -e Encrypt the input files. [default] |
| 37 | -t TAG Use TAG as the passphrase tag. |
| 38 | -o FILE Write the output to FILE. |
| 39 | EOF |
| 40 | #' |
| 41 | } |
| 42 | |
| 43 | sub gripe { print STDERR join(": ", $QUIS, @_), "\n"; $GRIPE = 1; } |
| 44 | sub barf { gripe(@_); exit(1); } |
| 45 | sub hexify { unpack("H*", join("", @_)); } |
| 46 | sub unhexify { my $x = join("", @_); $x =~ tr/\s//d; pack("H*", $x); } |
| 47 | |
| 48 | sub debug { |
| 49 | return unless $DEBUG; |
| 50 | my $what = shift(@_); |
| 51 | print STDERR |
| 52 | "debug: $what", |
| 53 | (@_ ? " = " . join(" ", map { "<" . hexify($_) . ">" } @_) : ""), |
| 54 | "\n"; |
| 55 | } |
| 56 | |
| 57 | sub encrypt { |
| 58 | my $salt = pack("N", time()) . $Catacomb::random->fill(6); |
| 59 | debug("salt", $salt); |
| 60 | my $tag = sprintf($TAG, hexify($salt)); |
| 61 | my $pass = Catacomb::Passphrase->verify($tag); |
| 62 | barf("passwords don't match") unless defined($pass); |
| 63 | open OUT, "> $OFILE" or barf("couldn't write file `$OFILE'", $!); |
| 64 | syswrite(OUT, $salt) or barf("error writing `$OFILE'", $!); |
| 65 | my $c = $Catacomb::Cipher::rc4->init($pass . $salt); |
| 66 | foreach my $f (@ARGV ? @ARGV : "-") { |
| 67 | open IN, $f or barf("couldn't read file `$f'", $!); |
| 68 | for (;;) { |
| 69 | my $buf; |
| 70 | my $rc = sysread(IN, $buf, 8192); |
| 71 | barf("error reading `$f'", $!) unless defined($rc); |
| 72 | last unless $rc; |
| 73 | syswrite(OUT, $c->encrypt($buf)) or barf("error writing `$OFILE'", $!); |
| 74 | } |
| 75 | close(IN); |
| 76 | } |
| 77 | close(OUT) or barf("error writing `$OFILE'", $!); |
| 78 | } |
| 79 | |
| 80 | sub decrypt { |
| 81 | open OUT, "> $OFILE" or barf("couldn't write file `$OFILE'", $!); |
| 82 | foreach my $f (@ARGV ? @ARGV : "-") { |
| 83 | open IN, $f or barf("couldn't read file `$f'", $!); |
| 84 | my ($salt, $buf); |
| 85 | my $rc = sysread(IN, $salt, 10); |
| 86 | barf("error reading `$f'", $!) unless defined($rc); |
| 87 | barf("ciphertext file is too short") unless $rc; |
| 88 | debug("salt", $salt); |
| 89 | my $tag = sprintf($TAG, hexify($salt)); |
| 90 | my $pass = Catacomb::Passphrase->read($tag) |
| 91 | or barf("couldn't read passphrase", $!); |
| 92 | my $c = $Catacomb::Cipher::rc4->init($pass . $salt); |
| 93 | for (;;) { |
| 94 | my $buf; |
| 95 | my $rc = sysread(IN, $buf, 8192); |
| 96 | barf("error reading `$f'", $!) unless defined($rc); |
| 97 | last unless $rc; |
| 98 | syswrite(OUT, $c->decrypt($buf)) or barf("error writing `$OFILE'", $!); |
| 99 | } |
| 100 | close(IN); |
| 101 | } |
| 102 | close(OUT) or barf("error writing `$OFILE'", $!); |
| 103 | } |
| 104 | |
| 105 | while (@ARGV) { |
| 106 | my $opt = $ARGV[0]; |
| 107 | last if $opt eq "-" || $opt =~ /^[^-]/; |
| 108 | shift(@ARGV); |
| 109 | last if $opt eq "--"; |
| 110 | $opt = substr($opt, 1); |
| 111 | while (length($opt)) { |
| 112 | my $o = substr($opt, 0, 1); |
| 113 | $opt = substr($opt, 1); |
| 114 | if ($o eq "o") { |
| 115 | $OFILE = length($opt) ? $opt : shift(@ARGV); $opt = ""; |
| 116 | gripe("option `-o' requires an argument") unless defined($OFILE); |
| 117 | } elsif ($o eq "d") { |
| 118 | $MODE = \&decrypt; |
| 119 | } elsif ($o eq "e") { |
| 120 | $MODE = \&encrypt; |
| 121 | } elsif ($o eq "t") { |
| 122 | $TAG = length($opt) ? $opt : shift(@ARGV); $opt = ""; |
| 123 | gripe("option `-t' requires an argument") unless defined($TAG); |
| 124 | } elsif ($o eq "h") { |
| 125 | help(\*STDOUT); |
| 126 | exit(0); |
| 127 | } elsif ($o eq "v") { |
| 128 | version(\*STDOUT); |
| 129 | exit(0); |
| 130 | } elsif ($o eq "u") { |
| 131 | usage(\*STDOUT); |
| 132 | exit(0); |
| 133 | } elsif ($o eq "D") { |
| 134 | $DEBUG = 1; |
| 135 | } else { |
| 136 | gripe("unknown option `-$o'"); |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | if ($GRIPE) { usage(\*STDERR); exit(1); } |
| 141 | |
| 142 | &$MODE(); |
| 143 | exit(0); |
| 144 | |