# Filename: Mcrypt.pm
# Author:   Theo Schlossnagle <jesus@omniti.com>
# Created:  17th January 2001
# Version:  2.5.7.0
#
# Copyright (c) 1999,2001,2007 Theo Schlossnagle. All rights reserved.
#   This program is free software; you can redistribute it and/or
#   modify it under the same terms as Perl itself.
#
#

package Mcrypt;

require 5.004;
require Exporter;
require DynaLoader;
require AutoLoader;
use Carp;

use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);

$VERSION = "2.5.7.0" ;

@ISA = qw(Exporter DynaLoader);


%EXPORT_TAGS = (
		ALGORITHMS => [ qw(BLOWFISH
				   DES
				   3DES
				   GOST
				   CAST_128
				   XTEA
				   RC2
				   TWOFISH
				   CAST_256
				   SAFERPLUS
				   LOKI97
				   SERPENT
				   RIJNDAEL_128
				   RIJNDAEL_192
				   RIJNDAEL_256
				   ENIGMA
				   ARCFOUR
				   WAKE) ],
		MODES => [ qw(CBC
			      ECB
			      CFB
			      OFB
			      nOFB
			      STREAM) ],
		FUNCS => [ qw(mcrypt_load
			      mcrypt_unload
			      mcrypt_init
			      mcrypt_end
			      mcrypt_encrypt
			      mcrypt_decrypt
			      mcrypt_get_block_size
			      mcrypt_get_iv_size
			      mcrypt_get_key_size) ],
	       );

@EXPORT = qw( ERROR );

@EXPORT_OK = qw(ERROR

		BLOWFISH
		DES
		3DES
		GOST
		CAST_128
		XTEA
		RC2
		TWOFISH
		CAST_256
		SAFERPLUS
		LOKI97
		SERPENT
		RIJNDAEL_128
		RIJNDAEL_192
		RIJNDAEL_256
		ENIGMA
		ARCFOUR
		WAKE

		CBC
		ECB
		CFB
		OFB
		nOFB
		STREAM

		mcrypt_load
		mcrypt_unload
		mcrypt_init
		mcrypt_end
		mcrypt_encrypt
		mcrypt_decrypt
		mcrypt_get_block_size
		mcrypt_get_iv_size
		mcrypt_get_key_size);

sub AUTOLOAD {
    # This AUTOLOAD is used to 'autoload' constants from the constant()
    # XS function.  If a constant is not found then control is passed
    # to the AUTOLOAD in AutoLoader.

    my $constname;
    ($constname = $AUTOLOAD) =~ s/.*:://;
    if($constname eq 'constant') {
        # This will recurse!
	croak "Problem loading Mcrypt libraries";
    }
    my $val = constant($constname, @_ ? $_[0] : 0);
    if ($! != 0) {
        if ($! =~ /Invalid/) {
            $AutoLoader::AUTOLOAD = $AUTOLOAD;
            goto &AutoLoader::AUTOLOAD;
        }
        else {
                croak "Your vendor has not defined Mcrypt macro $constname";
        }
    }
    eval "sub $AUTOLOAD { \"$val\" }";
    goto &$AUTOLOAD;
}

bootstrap Mcrypt $VERSION ;

sub new {
  my($self, %arg) = @_;
  my $class = ref($self) || $self;
  return undef unless defined($arg{'algorithm'});
  return undef  unless defined($arg{'mode'});
  $arg{'algorithm_dir'} = '' unless defined($arg{'algorithm_dir'});
  $arg{'mode_dir'} = '' unless defined($arg{'mode_dir'});
  my $td = mcrypt_load( $arg{'algorithm'}, $arg{'algorithm_dir'},
			$arg{'mode'}, $arg{'mode_dir'} );
  my $mcrypt = bless { TD => $td }, $class;
  $mcrypt->{Verbose} = 1;
  $mcrypt->{Verbose} = $arg{'verbose'} if(defined($arg{'verbose'}));
  $mcrypt->{KEY_SIZE} = mcrypt_get_key_size($td);
  $mcrypt->{IV_SIZE} = mcrypt_get_iv_size($td);
  $mcrypt->{BLOCK_SIZE} = -1; # default to not be in block mode
  $mcrypt->{BLOCK_SIZE} = mcrypt_get_block_size($td)
    if(mcrypt_is_block_algorithm_mode($td));
  return $mcrypt;
}

sub init {
  my($self, $key, $iv) = @_;
  if($self->{Verbose}) {
    print STDERR "Mcrypt: your initialization vector is the wrong length.\n"
      if($self->{Verbose} &&
	 ($self->{IV_SIZE} > 0) && ($self->{IV_SIZE} != length($iv)));
  }
  return ($self->{Initialized} = mcrypt_init($self->{TD}, $key, $iv));
}

sub end {
  my($self) = shift;
  my $ret = 0;
  $ret = mcrypt_end($self->{TD})
    if($self->{Initialized} && $self->{TD});
  $self->{TD} = 0;
  return $ret;
}

sub encrypt {
  my($self, $input) = @_;
  return undef unless($self->{Initialized});
  if($self->{Verbose}) {
    print STDERR
      "Mcrypt: running block mode and your input is not a valid length.\n"
	if($self->{Verbose} &&
	   ($self->{BLOCK_SIZE} > 0) &&
	   ($self->{BLOCK_SIZE} != length($input)));
  }
  return mcrypt_encrypt($self->{TD}, $input);
}

sub decrypt {
  my($self, $input) = @_;
  return undef unless($self->{Initialized});
  if($self->{Verbose}) {
    print STDERR
      "Mcrypt: running block mode and your input is not a valid length.\n"
	if($self->{Verbose} &&
	   ($self->{BLOCK_SIZE} > 0) &&
	   ($self->{BLOCK_SIZE} != length($input)));
  }
  return mcrypt_decrypt($self->{TD}, $input);
}

sub DESTROY {
  my($self) = shift;
  if($self->{Initialized}) {
    $self->end();
  } else {
    mcrypt_unload($self->{TD});
  }
}

1;

__END__

# Below is the stub of documentation for your module. You better edit it!

=head1 NAME

Mcrypt - Perl extension for the Mcrypt cryptography library

=head1 SYNOPSIS

  use Mcrypt;

  # Procedural routines

  $td = Mcrypt::mcrypt_load($algorithm, $algorithm_dir,
			    $mode, $mode_dir);

  Mcrypt::mcrypt_get_key_size($td);   # in bytes
  Mcrypt::mcrypt_get_iv_size($td);    # in bytes
  Mcrypt::mcrypt_get_block_size($td); # in bytes

  Mcrypt::mcrypt_init($td, $key, $iv);

  $encryptedstr = Mcrypt::mcrypt_encrypt($td, $decryptedstr);
  $decryptedstr = Mcrypt::mcrypt_decrypt($td, $encryptedstr);

  Mcrypt::mcrypt_end($td);

  # Object-oriented methods

  $td = Mcrypt->new( algorithm => $algorithm,
		     mode => $mode );

  $keysize = $td->{KEY_SIZE};
  $ivsize  = $td->{IV_SIZE};
  $blksize = $td->{BLOCK_SIZE};

  $td->init($key, $iv);

  $encryptedstr = $td->encrypt($decryptedstr);
  $decryptedstr = $td->decrypt($encryptedstr);

  # If the $td goes out of context,
  # the destructor will do this for you
  $td->end();

=head1 DESCRIPTION

This module wraps the libmcrypt encryption library for easy and convenient
use from within perl.  Encryption and decryption using a variety of algorithms
is as easy as a few simple lines of perl.

=head1 Exported constants

The predefined groups of exports in the use statements are as follows:

use Mcrypt qw(:ALGORITHMS);

Exports the BLOWFISH DES 3DES GOST 
CAST_128 XTEA RC2 TWOFISH CAST_256 SAFERPLUS LOKI97 SERPENT
RIJNDAEL_128 RIJNDAEL_192 RIJNDAEL_256 ENIGMA ARCFOUR WAKE libmcrypt
algorithms.  See the mcrypt(3) man page for more details.

use Mcrypt qw(:MODES);

Exports the CBC ECB CFB OFB bOFB STREAM modes of encryption.  See the
mcrypt(3) man page for more details.

use Mcrypt qw(:FUNCS);

Exports the following functions: mcrypt_load, mcrypt_unload,
mcrypt_init, mcrypt_end, mcrypt_encrypt, mcrypt_decrypt,
mcrypt_get_block_size, mcrypt_get_iv_size, mcrypt_get_key_size.

=head1 EXAMPLES

  # Procedural approach:
  # create an ecryption descriptor:
  #   ALGORITHM: blowfish (256 bit key + 16 byte IV)
  #   MODE:      cfb
  # The user application has set:
  #   $method to either "encrypt" or "decrypt"
  #   $infile to the input filename
  #   $outfile to the output filename
  my($td) = Mcrypt::mcrypt_load( Mcrypt::BLOWFISH, '',
				 Mcrypt::CFB, '' );
  my($key) = "32 bytes of your apps secret key";  # secret key
  my($iv) = "16 bytes of rand"; # shared initialization vector
  Mcrypt::mcrypt_init($td, $key, $iv) || die "Could not initialize td";
  print Mcrypt::mcrypt_encrypt($td, $_) while(<>);
  Mcrypt::mcrypt_end($td);

  # OO approach of the above except decrypting
  my($td) = Mcrypt->new( algorithm => Mcrypt::BLOWFISH,
                         mode => Mcrypt::CFB,
                         verbose => 0 );
  my($key) = "k" x $td->{KEY_SIZE};
  my($iv) = "i" x $td->{IV_SIZE};
  $td->init($key, $iv);
  print $td->decrypt($_) while (<>);
  $td->end();

=head1 AUTHOR

Theo Schlossnagle <jesus@omniti.com>

=head1 SEE ALSO

The libmcrypt man page: mcrypt(3).  Other libmcrypt information is
available at http://mcrypt.hellug.gr/.

=cut