version 1.62, 2008/09/19 07:01:00 |
version 1.64, 2011/09/19 04:05:11 |
|
|
blocksize => 16, |
blocksize => 16, |
default_iter => 250, |
default_iter => 250, |
}, |
}, |
|
{ |
|
alias => 'TESTING', |
|
name => 'Testing', |
|
keylen => 8, |
|
blocksize => 1, |
|
default_iter => 1, |
|
}, |
); |
); |
|
|
my %LABELS = ( |
my %LABELS = ( |
|
|
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
} |
} |
# XXX Should I? |
# XXX Should I? |
delete $rec->{plaintext}; |
#delete $rec->{plaintext}; |
delete $rec->{encrypted}; |
#delete $rec->{encrypted}; |
|
|
croak 'No data in record to pack' if !$rec->{data}; |
croak 'No data in record to pack' if !$rec->{data}; |
|
|
|
|
sub Encrypt |
sub Encrypt |
{ |
{ |
my $self = shift; |
my $self = shift; |
my $rec = shift; |
my $rec = shift || croak('Needed parameter [record] not passed!'); |
my $pass = shift || $self->{password}; |
my $pass = shift || $self->{password}; |
|
|
if ( !$rec ) { |
|
croak('Needed parameter [record] not passed!'); |
|
} |
|
|
|
my $data = shift || $rec->{plaintext}; |
my $data = shift || $rec->{plaintext}; |
my $ivec = shift; |
my $ivec = shift; |
|
|
|
$self->_password_verify($pass); |
|
|
if ( ! $pass && ! $self->{appinfo}->{key}) { |
if ( !$data ) { croak('Needed parameter [plaintext] not passed!'); } |
croak('password not set!'); |
|
} |
|
|
|
if ( ! $data) { |
|
croak('Needed parameter [plaintext] not passed!'); |
|
} |
|
|
|
if ( $pass && ! $self->Password($pass)) { |
|
croak('Incorrect Password!'); |
|
} |
|
|
|
my $acct; |
my $acct; |
if ($rec->{encrypted}) { |
if ($rec->{encrypted}) { |
$acct = $self->Decrypt($rec, $pass); |
$acct = $self->Decrypt($rec, $pass); |
|
|
$encrypted = _encrypt_v4($datav4, $acctv4, $self->{digest}); |
$encrypted = _encrypt_v4($datav4, $acctv4, $self->{digest}); |
|
|
} elsif ($self->{version} == 5) { |
} elsif ($self->{version} == 5) { |
($encrypted, $rec->{ivec}) = _encrypt_v5( |
($encrypted, $ivec) = _encrypt_v5( |
$data, $acct, |
$data, $acct, |
$self->{appinfo}->{key}, |
$self->{appinfo}->{key}, |
$self->{appinfo}->{cipher}, |
$self->{appinfo}->{cipher}, |
$ivec, |
$ivec, |
); |
); |
|
$rec->{ivec} = $ivec if $ivec; |
|
|
} else { |
} else { |
croak "Unsupported version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
} |
} |
|
|
$rec->{plaintext}->{0} = $data->{0}; |
$rec->{plaintext}->{0} = $data->{0}; |
|
|
my $c = crypts($cipher) or croak('Unknown cipher ' . $cipher); |
my $c = crypts($cipher) or croak('Unknown cipher ' . $cipher); |
|
|
if (! defined $ivec) { |
if (! defined $ivec) { |
while (! $ivec) { |
if (!$c->{blocksize}) { |
$ivec = pack("C*",map {rand(256)} 1..$c->{blocksize}); |
$ivec = $EMPTY; |
} |
} |
|
else { |
|
while (! $ivec) { |
|
$ivec = pack("C*",map {rand(256)} 1..$c->{blocksize}); |
|
} |
|
} |
} |
} |
|
|
my $changed = 0; |
my $changed = 0; |
|
|
if ($new->{3}->{data}) { |
if ($new->{3}->{data}) { |
$need_newdate = 0; |
$need_newdate = 0; |
} |
} |
foreach my $k (keys %{ $new }) { |
|
if (! $old) { |
|
$changed = 1; |
|
} elsif ($k == 3) { |
|
if ($old && ( |
|
$new->{$k}{data}{day} == $old->{$k}{data}{day} && |
|
$new->{$k}{data}{month} == $old->{$k}{data}{month} && |
|
$new->{$k}{data}{year} == $old->{$k}{data}{year} |
|
)) { |
|
$changed = 1; |
|
$need_newdate = 1; |
|
} |
|
|
|
} else { |
if ($old) { |
my $n = join ':', sort %{ $new->{$k} }; |
foreach my $k (keys %{ $new }) { |
my $o = join ':', sort %{ $old->{$k} }; |
if (! $old->{$k} ) { |
if ($n ne $o) { |
|
$changed = 1; |
$changed = 1; |
|
last; |
|
} |
|
if (! $new->{$k}) { |
|
$changed = 1; |
|
last; |
} |
} |
|
elsif ($k == 3) { |
|
if (! $new->{$k}->{data} && $old->{$k}->{data} ) { |
|
$changed = 1; |
|
last; |
|
} |
|
|
|
my %n = %{ $new->{$k}->{data} }; |
|
my %o = %{ $old->{$k}->{data} }; |
|
|
|
foreach (qw( day month year )) { |
|
$n{$_} ||= 0; |
|
$o{$_} ||= 0; |
|
} |
|
|
|
if ( |
|
$n{day} == $o{day} && |
|
$n{month} == $o{month} && |
|
$n{year} == $o{year} |
|
) { |
|
$need_newdate = 1; |
|
} |
|
else { |
|
$changed = 1; |
|
last; |
|
} |
|
|
|
} |
|
else { |
|
my $n = join ':', sort %{ $new->{$k} }; |
|
my $o = join ':', sort %{ $old->{$k} }; |
|
if ($n ne $o) { |
|
$changed = 1; |
|
last; |
|
} |
|
} |
} |
} |
} |
} |
|
else { |
|
$changed = 1; |
|
} |
|
|
return (1, $ivec) if $changed == 0; |
return 1 if $changed == 0; |
|
|
if ($need_newdate) { |
if ($need_newdate) { |
my ($day, $month, $year) = (localtime)[3,4,5]; |
my ($day, $month, $year) = (localtime)[3,4,5]; |
|
|
-blocksize => $c->{blocksize}, |
-blocksize => $c->{blocksize}, |
-header => 'none', |
-header => 'none', |
-padding => 'oneandzeroes', |
-padding => 'oneandzeroes', |
); |
) || croak("Unable to set up encryption!"); |
|
|
if (! $c) { |
|
croak("Unable to set up encryption!"); |
|
} |
|
|
|
$encrypted = $cbc->encrypt($plaintext); |
$encrypted = $cbc->encrypt($plaintext); |
|
|
} else { |
} else { |
|
|
my $rec = shift; |
my $rec = shift; |
my $pass = shift || $self->{password}; |
my $pass = shift || $self->{password}; |
|
|
if ( ! $pass && ! $self->{appinfo}->{key}) { |
if ( ! $rec) { croak('Needed parameter [record] not passed!'); } |
croak("password not set!\n"); |
if ( ! $rec->{encrypted} ) { croak('No encrypted content!'); } |
} |
|
|
|
if ( ! $rec) { |
$self->_password_verify($pass); |
croak("Needed parameter 'record' not passed!\n"); |
|
} |
|
|
|
if ( $pass && ! $self->Password($pass)) { |
|
croak("Invalid Password!\n"); |
|
} |
|
|
|
if ( ! $rec->{encrypted} ) { |
|
croak("No encrypted content!"); |
|
} |
|
|
|
my $plaintext; |
my $plaintext; |
if ($self->{version} == 4) { |
if ($self->{version} == 4) { |
$self->{digest} ||= _calc_keys( $pass ); |
$self->{digest} ||= _calc_keys( $pass ); |
|
|
-blocksize => $c->{blocksize}, |
-blocksize => $c->{blocksize}, |
-header => 'none', |
-header => 'none', |
-padding => 'oneandzeroes', |
-padding => 'oneandzeroes', |
); |
) || croak("Unable to set up encryption!"); |
|
|
if (! $c) { |
|
croak("Unable to set up encryption!"); |
|
} |
|
my $len = $c->{blocksize} - length($encrypted) % $c->{blocksize}; |
my $len = $c->{blocksize} - length($encrypted) % $c->{blocksize}; |
$encrypted .= $NULL x $len; |
$encrypted .= $NULL x $len; |
$plaintext = $cbc->decrypt($encrypted); |
$plaintext = $cbc->decrypt($encrypted); |
|
|
} |
} |
} |
} |
|
|
|
return $self->_password_verify($pass); |
|
} |
|
|
|
sub _password_verify { |
|
my $self = shift; |
|
my $pass = shift; |
|
if (!defined $pass) { |
|
$pass = $self->{password}; |
|
} |
|
|
|
if ( !$pass ) { |
|
croak("Password not set!\n"); |
|
} |
|
|
if (defined $self->{password} && $pass eq $self->{password}) { |
if (defined $self->{password} && $pass eq $self->{password}) { |
# already verified this password |
# already verified this password |
return 1; |
return 1; |
} |
} |
|
|
if ($self->{version} == 4) { |
if ($self->{version} == 4) { |
my $valid = _password_verify_v4($pass, $self->{encpassword}); |
_password_verify_v4($pass, $self->{encpassword}); |
|
|
# May as well generate the keys we need now, |
# May as well generate the keys we need now, |
# since we know the password is right |
# since we know the password is right |
if ($valid) { |
$self->{digest} = _calc_keys($pass); |
$self->{digest} = _calc_keys($pass); |
$self->{password} = $pass; |
if ($self->{digest} ) { |
return 1; |
$self->{password} = $pass; |
|
return 1; |
|
} |
|
} |
|
} elsif ($self->{version} == 5) { |
} elsif ($self->{version} == 5) { |
return _password_verify_v5($self->{appinfo}, $pass); |
_password_verify_v5($self->{appinfo}, $pass); |
} else { |
$self->{password} = $pass; |
croak "Unsupported version $self->{version}"; |
return 1; |
} |
} |
|
|
return; |
croak "Unsupported Version $self->{version}"; |
} |
} |
|
|
sub _password_verify_v4 |
sub _password_verify_v4 |
|
|
my $pass = shift; |
my $pass = shift; |
my $data = shift; |
my $data = shift; |
|
|
if (! $pass) { croak('No password specified!'); }; |
if (! $pass) { croak('No password specified!'); } |
|
if (! $data) { croak('No encrypted password in file!'); } |
|
|
# XXX die "No encrypted password in file!" unless defined $data; |
|
if ( ! defined $data) { return; }; |
|
|
|
$data =~ s/$NULL$//xm; |
$data =~ s/$NULL$//xm; |
|
|
my $salt = substr $data, 0, $kSalt_Size; |
my $salt = substr $data, 0, $kSalt_Size; |
|
|
my $digest = md5($msg); |
my $digest = md5($msg); |
|
|
if ($data ne $salt . $digest ) { |
if ($data ne $salt . $digest ) { |
return; |
croak("Incorrect Password!"); |
} |
} |
|
|
return 1; |
return 1; |
|
|
#print "Hash: '". $hash . "'\n"; |
#print "Hash: '". $hash . "'\n"; |
#print "Hash: '". $appinfo->{masterhash} . "'\n"; |
#print "Hash: '". $appinfo->{masterhash} . "'\n"; |
|
|
if ($appinfo->{masterhash} eq $hash) { |
if ($appinfo->{masterhash} ne $hash) { |
$appinfo->{key} = $key; |
croak("Incorrect Password!"); |
} else { |
|
return; |
|
} |
} |
|
|
return $key; |
$appinfo->{key} = $key; |
|
return 1; |
} |
} |
|
|
|
|
|
|
if ($self->{version} == 4) { |
if ($self->{version} == 4) { |
my $data = _password_update_v4($pass, @_); |
my $data = _password_update_v4($pass, @_); |
|
|
if (! $data) { |
if (! $data) { croak "Failed to update password!"; } |
carp("Failed to update password!"); |
|
return; |
|
} |
|
|
|
# AFAIK the thing we use to test the password is |
# AFAIK the thing we use to test the password is |
# always in the first entry |
# always in the first entry |
|
|
$self->{appinfo}, $pass, $cipher, $iter, $salt |
$self->{appinfo}, $pass, $cipher, $iter, $salt |
); |
); |
|
|
if (! $hash) { |
if (! $hash) { croak "Failed to update password!"; } |
carp("Failed to update password!"); |
|
return; |
|
} |
|
|
|
|
$self->{password} = $pass; |
|
|
return 1; |
return 1; |
} else { |
|
croak("Unsupported version ($self->{version})"); |
|
} |
} |
|
|
return; |
croak "Unsupported Version $self->{version}"; |
} |
} |
|
|
sub _password_update_v4 |
sub _password_update_v4 |
|
|
my ($pass) = @_; |
my ($pass) = @_; |
$pass ||= $self->{password}; |
$pass ||= $self->{password}; |
|
|
if ( $pass && ! $self->Password($pass)) { |
$self->_password_verify($pass); |
croak("Invalid Password!\n"); |
|
} |
|
|
|
foreach my $rec (@{ $self->{records} }) { |
foreach my $rec (@{ $self->{records} }) { |
$self->Decrypt($rec); |
$self->Decrypt($rec); |
|
|
|
|
sub _calc_keys |
sub _calc_keys |
{ |
{ |
|
require Digest::MD5; |
|
import Digest::MD5 qw(md5); |
|
|
my $pass = shift; |
my $pass = shift; |
if (! defined $pass) { croak('No password defined!'); }; |
if (! defined $pass) { croak('No password defined!'); }; |
|
|