#!/usr/bin/perl -s
##
## Tie::EncryptedHash - A tied hash with encrypted fields.
##
## Copyright (c) 2000, Vipul Ved Prakash. All rights reserved.
## This code is based on Damian Conway's Tie::SecureHash.
##
## $Id: EncryptedHash.pm,v 1.8 2000/09/02 19:23:00 vipul Exp vipul $
## vi:expandtab=1;ts=4;
package Tie::EncryptedHash;
use strict;
use vars qw($VERSION $strict);
use Digest::MD5 qw(md5_base64);
use Crypt::CBC;
use Data::Dumper;
use Carp;
( $VERSION ) = '$Revision: 1.8 $' =~ /\s(\d+\.\d+)\s/;
my $DEBUG = 0;
sub debug {
return undef unless $DEBUG;
my ($caller, undef) = caller;
my (undef,undef,$line,$sub) = caller(1); $sub =~ s/.*://;
$sub = sprintf "%10s()%4d",$sub,$line;
print "$sub " . (shift) . "\n";
}
# sub new {
# my ($class,%args) = @_;
# my %self = (); tie %self, $class;
# my $self = bless \%self, $class;
# $self->{__password} = $args{__password} if $args{__password};
# $self->{__cipher} = $args{__cipher} || qq{Blowfish};
# return $self;
# }
sub new {
my ($class,%args) = @_;
my $self = {}; tie %$self, $class;
bless $self, $class;
$self->{__password} = $args{__password} if $args{__password};
$self->{__cipher} = $args{__cipher} || qq{Blowfish};
return $self;
}
sub _access {
my ($self, $key, $caller, $file, $value, $delete) = @_;
my $class = ref $self || $self;
# SPECIAL ATTRIBUTE
if ( $key =~ /(__password|__hide|__scaffolding|__cipher)$/ ) {
my $key = $1;
unless($value||$delete) {return undef unless $caller eq $class
+}
if ($delete && ($key =~ /__password/)) {
for (keys %{$$self{__scaffolding}}) {
if ( ref $self->{$_} ) {
$self->{$_} = encrypt($self->{$_}, $self->{__scaffolding
+}{$_}, $self->{__cipher});
delete $self->{__scaffolding}{$_};
}
}
}
delete $$self{$key} if $delete;
return $self->{$key} = $value if $value;
return $self->{$key};
# SECRET FIELD
} elsif ( $key =~ m/^(_{1}[^_][^:]*)$/ ||$key =~ m/.*?::(_{1}[^_][
+^:]*)/ ) {
my $ctext = $self->{$1};
if ( ref $ctext && !($value)) { # ENCRYPT REF AT FETCH
my $pass = $self->{__scaffolding}{$1} || $self->{__password}
+;
return undef unless $pass;
$self->{$1} = encrypt($ctext, $pass, $self->{__cipher});
return $self->FETCH ($1);
}
my $ptext = qq{}; my $isnot = !( exists $self->{$1} );
my $auth = verify($self,$1);
return undef if !($auth) && ref $self->{$1};
return undef if !($auth) && $self->{__hide};
if ($auth && $auth ne "1") { $ptext = $auth }
if ($value && $auth) { # STORE
if ( ref $value ) {
$self->{__scaffolding}{$1} = $self->{__password}; $cte
+xt = $value;
} else {
my $key = $1;
unless ($self->{__password}) {
if ($value =~ m:^\S+\s\S{22}\s:) {
return $self->{$key} = $value;
} else { return undef }
}
$ctext = encrypt($value, $self->{__password}, $self->{__
+cipher});
}
$self->{$1} = $ctext;
return $value;
} elsif ($auth && $delete) { # DELETE
delete $$self{$1}
} elsif ($isnot && (!($value))) { # DOESN'T EXIST
return;
} elsif ((!($auth)) && $ctext) {
return $ctext; # FETCH return ciphertext
} elsif ($auth && !($isnot)) { # FETCH return plaintext
if (ref $ptext) {
$self->{$1} = $ptext;
$self->{__scaffolding}{$1} = $self->{__password}; # R
+ef counting mechanism
return $self->{$1};
}
}
return undef unless $auth;
return $ptext;
# PUBLIC FIELD
} elsif ( $key =~ m/([^:]*)$/ || $key =~ m/.*?::([^:]*)/ ) {
$self->{$1} = $value if $value;
delete $$self{$1} if $delete;
return $self->{$1} if $self->{$1};
return undef;
}
}
sub encrypt { # ($plaintext, $password, $cipher)
$_[0] = qq{REF }. Data::Dumper->new([$_[0]])->Indent(0)->Terse(0)-
+>Purity(1)->Dumpxs if ref $_[0];
return qq{$_[2] } . md5_base64($_[0]) .qq{ } .
Crypt::CBC->new($_[1],$_[2])->encrypt_hex($_[0])
}
sub decrypt { # ($cipher $md5sum $ciphertext, $password)
return undef unless $_[1];
my ($m, $d, $c) = split /\s/,$_[0];
my $ptext = Crypt::CBC->new($_[1],$m)->decrypt_hex($c);
my $check = md5_base64($ptext);
if ( $d eq $check ) {
if ($ptext =~ /^REF (.*)/is) {
my ($VAR1,$VAR2,$VAR3,$VAR4,$VAR5,$VAR6,$VAR7,$VAR8);
return eval qq{$1};
}
return $ptext;
}
}
sub verify { # ($self, $key)
my ($self, $key) = splice @_,0,2;
# debug ("$self->{__scaffolding}{$key}, $self->{__password}, $self
+->{$key}");
return 1 unless $key =~ m:^_:;
return 1 unless exists $self->{$key};
return undef if ref $self->{$key} && ($self->{__scaffolding}{$key}
+ ne
$self->{__password});
my $ptext = decrypt($self->{$key}, $self->{__password});
return $ptext if $ptext;
}
sub each { CORE::each %{$_[0]} }
sub keys { CORE::keys %{$_[0]} }
sub values { CORE::values %{$_[0]} }
sub exists { CORE::exists $_[0]->{$_[1]} }
sub TIEHASH # ($class, @args)
{
my $class = ref($_[0]) || $_[0];
my $self = bless {}, $class;
$self->{__password} = $_[1] if $_[1];
$self->{__cipher} = $_[2] || qq{Blowfish};
return $self;
}
sub FETCH # ($self, $key)
{
my ($self, $key) = @_;
my $entry = _access($self,$key,(caller)[0..1]);
return $entry if $entry;
}
sub STORE # ($self, $key, $value)
{
my ($self, $key, $value) = @_;
my $entry = _access($self,$key,(caller)[0..1],$value);
return $entry if $entry;
}
sub DELETE # ($self, $key)
{
my ($self, $key) = @_;
return _access($self,$key,(caller)[0..1],'',1);
}
sub CLEAR # ($self)
{
my ($self) = @_;
return undef if grep { ! $self->verify($_) }
grep { ! /__/ } CORE::keys %{$self};
%{$self} = ();
}
sub EXISTS # ($self, $key)
{
my ($self, $key) = @_;
my @context = (caller)[0..1];
return _access($self,$key,@context) ? 1 : '';
}
sub FIRSTKEY # ($self)
{
my ($self) = @_;
CORE::keys %{$self};
goto &NEXTKEY;
}
sub NEXTKEY # ($self)
{
my $self = $_[0]; my $key;
my @context = (caller)[0..1];
while (defined($key = CORE::each %{$self})) {
last if eval { _access($self,$key,@context) }
}
return $key;
}
sub DESTROY # ($self)
{
}
1;
__END__
|