chiark / gitweb /
infra: Add a copy of the GPL.
[catacomb-perl] / ciphersaber
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