# $Id: Blowfish.pm,v 1.14 2001/05/08 02:55:40 btrott Exp $

package Net::SSH::Perl::Cipher::Blowfish;

use strict;

use Net::SSH::Perl::Cipher;
use base qw( Net::SSH::Perl::Cipher );

use Net::SSH::Perl::Cipher::CBC;

use vars qw( $BF_CLASS );
BEGIN {
    my @err;
    for my $mod (qw( Crypt::Blowfish Crypt::Blowfish_PP )) {
        eval "use $mod;";
        $BF_CLASS = $mod, last unless $@;
        push @err, $@;
    }
    die "Failed to load Crypt::Blowfish and Crypt::Blowfish_PP: @err"
        unless $BF_CLASS;
}

sub new {
    my $class = shift;
    my $ciph = bless { }, $class;
    $ciph->init(@_) if @_;
    $ciph;
}

sub keysize { 16 }
sub blocksize { 8 }

sub init {
    my $ciph = shift;
    my($key, $iv, $is_ssh2) = @_;
    my $blow = $BF_CLASS->new($is_ssh2 ? substr($key, 0, 16) : $key);
    $ciph->{cbc} = Net::SSH::Perl::Cipher::CBC->new($blow,
        $iv ? substr($iv, 0, 8) : undef);
    $ciph->{is_ssh2} = defined $is_ssh2 ? $is_ssh2 : 0;
}

sub encrypt {
    my($ciph, $text) = @_;
    $ciph->{is_ssh2} ?
        $ciph->{cbc}->encrypt($text) :
        _swap_bytes($ciph->{cbc}->encrypt(_swap_bytes($text)));
}

sub decrypt {
    my($ciph, $text) = @_;
    $ciph->{is_ssh2} ?
        $ciph->{cbc}->decrypt($text) :
        _swap_bytes($ciph->{cbc}->decrypt(_swap_bytes($text)));
}

sub _swap_bytes {
    my $str = $_[0];
    $str =~ s/(.{4})/reverse $1/sge;
    $str;
}

1;
__END__

=head1 NAME

Net::SSH::Perl::Cipher::Blowfish - Wrapper for SSH Blowfish support

=head1 SYNOPSIS

    use Net::SSH::Perl::Cipher;
    my $cipher = Net::SSH::Perl::Cipher->new('Blowfish', $key);
    print $cipher->encrypt($plaintext);

=head1 DESCRIPTION

I<Net::SSH::Perl::Cipher::Blowfish> provides Blowfish encryption
support for I<Net::SSH::Perl>. To do so it wraps around either
I<Crypt::Blowfish> or I<Crypt::Blowfish_PP>; the former is a
C/XS implementation of the blowfish algorithm, and the latter is
a Perl implementation. I<Net::SSH::Perl::Cipher::Blowfish> prefers
to use I<Crypt::Blowfish>, because it's faster, so we try to load
that first. If it fails, we fall back to I<Crypt::Blowfish_PP>.
Note that, when using I<Crypt::Blowfish_PP>, you'll experience
a very noticeable decrease in performance.

The blowfish used here is in CBC filter mode with a key length
of 32 bytes.

SSH1 adds an extra wrinkle with respect to its blowfish algorithm:
before and after encryption/decryption, we have to swap the bytes
in the string to be encrypted/decrypted. The byte-swapping is done
four bytes at a time, and within each of those four-byte blocks
we reverse the bytes. So, for example, the string C<foobarba>
turns into C<boofabra>. We swap the bytes in this manner in the
string before we encrypt/decrypt it, and swap the
encrypted/decrypted string again when we get it back.

This byte-swapping is not done when Blowfish is used in the
SSH2 protocol.

=head1 AUTHOR & COPYRIGHTS

Please see the Net::SSH::Perl manpage for author, copyright,
and license information.

=cut
