On Thu, 13 Feb 2003, Neil Fryer wrote:

> Hi All
> 
> I found this script on the net, and I am still learning, Perl, but I was
> wondering, just to play around with, if this script encrypts, how would I
> decrypt?

No.

See attached.

S.

-- 
Shevek
I am the Borg.

sub AUTOLOAD{my$i=$AUTOLOAD;my$x=shift;$i=~s/^.*://;print"$x\n";eval
qq{*$AUTOLOAD=sub{my\$x=shift;return unless \$x%$i;&{$x}(\$x);};};}

foreach my $i (3..65535) { &{'2'}($i); }
#!/usr/bin/perl

# Rijndael encrypt and base64 encode data.
# Usage: cmd [-e | -d] file ....

use strict;
use Crypt::Rijndael;
use MIME::Base64;
use POSIX;
use IPC::Open3;
use Digest::MD5 qw(md5_hex);
use Symbol;
use IO::Handle;
use Text::Wrap;
use Data::Dumper;

my $ESUFFIX = "rijndael";               # filename suffix - change if you want
my $DSUFFIX = "decrypted";              # filename suffix - change if you want
my $KEYLEN = 128;                               # key bits - do not change
my $REQPASS = $KEYLEN / 8;              # required password length
my $MINPASS = $REQPASS / 2;             # minimum password bytes - half key

sub encrypt_rijndael {
        my ($password, $data) = @_;

        die "Data is already encrypted" if $data =~ /^XX-Encrypted/;

        my $engine = new Crypt::Rijndael $password,
                                        Crypt::Rijndael::MODE_CBC;

        my @header = (
                "XX-Encrypted",
                "Algorithm: Rijndael",
                "Length: " . length($data),
                "DataMD5: " . md5_hex($data),
                "Notes: Use MIME::Base64 then Crypt::Rijndael to recover data",
                # "PasswordMD5: " . md5_hex($password),
                );

        my $header = join("\n", @header) . "\n\n";

        my $blocksize = $engine->blocksize;
        my $padlen = $blocksize - (length($data) % $blocksize);
        if ($padlen) {
                print "Padding data to blocksize $blocksize " .
                                                "with $padlen bytes.\n";
                $data .= " " x $padlen;
        }
        $data = $engine->encrypt($data);
        $data = $header . encode_base64($data);

        return $data;
}

sub decrypt_rijndael {
        my ($password, $data) = @_;

        my $engine = new Crypt::Rijndael $password,
                                        Crypt::Rijndael::MODE_CBC;

        $data =~ s/^XX-Encrypted\n//ms
                        or die "File does not appear to be encrypted";

        $data =~ /^(.*?)\n\n(.*)/ms
                        or die "Could find separator between header and data";

        my $header = $1; $data = $2;
        my %header;

        foreach (split(/\n/, $header)) {
                $_ =~ /^([^:]*):\s*(.*)$/
                                or die "Invalid header line '$_'";
                $header{$1} = $2;
        }

        if (exists $header{Algorithm}) {
                if ($header{Algorithm} ne 'Rijndael') {
                        die "Algorithm incorrect: only support Rijndael.";
                }
        }

        if (exists $header{PasswordMD5}) {
                if (md5_hex($password) ne $header{PasswordMD5}) {
                        die "Password incorrect: does not match hash in header.";
                }
        }

        my ($cipher, $length) = ($1, $2);
        $data = decode_base64($data);
        $data = $engine->decrypt($data);
        if (exists $header{Length}) {
                if (length($data) > $header{Length}) {
                        my $padlen = length($data) - $header{Length};
                        print "Trimming data by $padlen bytes.\n";
                        $data = substr($data, 0, $header{Length});
                }
        }

        if (exists $header{DataMD5}) {
                if (md5_hex($data) ne $header{DataMD5}) {
                        die "Data incorrect: does not match hash in header. " .
                                                        "Perhaps incorrect password?";
                }
        }

        return $data;
}

sub filter_external {
        my ($data, $cmd, @args) = @_;

        die "External filter command '$cmd' does not exist." unless -f $cmd;
        die "External filter command '$cmd' not executable." unless -x $cmd;

        my ($rdr, $wtr, $err);
        $err = gensym;

        my $childpid = open3($wtr, $rdr, $err, $cmd, @args);

        print $wtr $data;
        close $wtr;

        my $out = <$rdr>;
        my $errs = <$err>;

        waitpid $childpid, 0;

        die "External filter command '$cmd' returned error:\n$errs"
                                        if $errs;

        return $out;
}

# For good measure, let's throw in a sub.
sub readpass {
        my $prompt = shift;

        print $prompt;

        my $termios = new POSIX::Termios;
        $termios->getattr(fileno(STDIN))
                        or die "Failed to get terminal parameters: $!";
        my $lflag = $termios->getlflag;
        $termios->setlflag($lflag & ~ECHO);
        $termios->setattr(fileno(STDIN), TCSANOW)
                        or die "Failed to set terminal parameters: $!";

        my $password = <STDIN>;
        chomp($password);

        $termios->setlflag($lflag);
        $termios->setattr(fileno(STDIN), TCSANOW)
                        or die "Failed to set terminal parameters: $!";

        print "\n";
        return $password;
}

sub getpassword {
        my ($op) = @_;

        my $padpass = 1 unless $op eq 'r';

        my $password;

        while (1) {
                $password = readpass("Password: ");
                # Allow decoding of old files with short passwords
                last if ($op ne 'e') || (length $password >= $MINPASS);
                print "Length of password must be at least $MINPASS characters.\n";
        }

        if ($op eq 'e') {
                while (1) {
                        my $confirm = readpass("Password (confirm): ");
                        last if $confirm eq $password;
                        print "Passwords do not match.\n";
                        exit 1;
                }
        }

        if ($op =~ /^[ed]$/) {
                if (length $password < $REQPASS) {
                        print "Padding password to $REQPASS bytes for Rijndael\n";
                        $password .= "\0" x ($KEYLEN / 8);      # 8 bits per byte
                        $password = substr($password, 0, ($KEYLEN / 8));
                }
        }

        return $password;
}

sub slurp {
        my $file = shift;

        print "Reading file '$file'\n";

        die "File '$file' not found"    unless -f $file;
        die "File '$file' not readable" unless -r $file;

        local $/ = undef;
        local *FH;

        open(FH, "<$file")      or die "Failed to open '$file' for read: $!";
        my $data = <FH>;
        close(FH)                       or die "Failed to close '$file': $!";

        return $data;
}

sub spew {
        my ($file, $data, $op) = @_;

        print "Writing file '$file'\n";

        # Overwrite only if encrypting
        my $overwrite = ($op eq 'e');

        if (-f $file) {
                die "Output file '$file' already exists" unless $overwrite;
                unlink($file) or die "Failed to remove old file '$file': $!";
        }

        local *FH;

        open(FH, ">$file")      or die "Failed to open '$file' for write: $!";
        print FH $data          or die "Failed to write to '$file': $!";
        close(FH)                       or die "Failed to close '$file': $!";
}

sub usage {
        my %flags = (
                e       => "Encrypt <file> using Rijndael's algorithm, " .
                                "save as '<file>.$ESUFFIX' and remove <file>.",
                d       => "Decrypt <file> using Rijndael's algorithm, " .
                                "removing the suffix '.$ESUFFIX' or adding " .
                                "'.$DSUFFIX' if <file> does not end in '.$ESUFFIX'. " 
.
                                "The target file must NOT exist.",
                h       => "Display this help.",
                        );
        print "Usage: $0 [-e | -d | -r | -h] <file> ...\n";
        print "Using Rijndael CBC mode, $KEYLEN bit key, " .
                                        "passwords must be at least $MINPASS chars\n";

        # Gratuitous use of technology...
        foreach (sort keys %flags) {
                print wrap("\t-$_\t", "\t" x 2, $flags{$_}) . "\n";
        }

        print wrap("", "", "Multiple files may be specified on the " .
                        "command line. The same operation will be performed " .
                        "on each with the same key.") . "\n";
}

sub main {
        my $op = shift @ARGV;
        unless ($op =~ s/^-([edr])$/$1/) {      # Not -h (hack!)
                usage;
                exit 1;
        }

        defined umask 0077
                        or die "Failed to set umask: Please get a proper Unix.";

        my $password = getpassword($op);
        my $outfile;

        foreach (@ARGV) {
                eval {  # Generic exception trap
                        my $data = slurp $_;

                        if ($op eq 'e') {
                                $data = encrypt_rijndael($password, $data);
                                $outfile = "$_.$ESUFFIX";
                        }
                        elsif ($op eq 'd') {
                                $data = decrypt_rijndael($password, $data);
                                $outfile = $_;
                                $outfile =~ s/\.$ESUFFIX$//o
                                        or
                                                $outfile .= ".$DSUFFIX";
                        }
                        else {
                                die "FATAL: Unknown operator $op!";
                        }

                        spew $outfile, $data, $op;
                        if ($op eq 'e') {
                                unlink($_)
                                        or die "Failed to remove plaintext file '$_': 
$!";
                        }
                };
                if ($@) {
                        my $err = $@;
                        $err =~ s/ at [^\s]* line \d+(?:,.*)?\.//;
                        print "While processing file $_, encountered error:\n" .
                                        "\t$err";
                }
        }
}

main;

Reply via email to