version 1.64, 2011/09/19 04:05:11 |
version 1.65, 2011/09/19 04:23:37 |
|
|
package Palm::Keyring; |
package Palm::Keyring; |
# $RedRiver: Keyring.pm,v 1.61 2008/09/19 05:55:35 andrew Exp $ |
# $RedRiver: Keyring.pm,v 1.62 2008/09/19 06:01:00 andrew Exp $ |
######################################################################## |
######################################################################## |
# Keyring.pm *** Perl class for Keyring for Palm OS databases. |
# Keyring.pm *** Perl class for Keyring for Palm OS databases. |
# |
# |
|
|
blocksize => 16, |
blocksize => 16, |
default_iter => 250, |
default_iter => 250, |
}, |
}, |
{ |
{ # Only for testing |
alias => 'TESTING', |
alias => 'TESTING', |
name => 'Testing', |
name => 'Testing', |
keylen => 8, |
keylen => 0, |
blocksize => 1, |
blocksize => 0, |
default_iter => 1, |
default_iter => 0, |
}, |
}, |
); |
); |
|
|
|
|
$rec->{data} = join $NULL, $name, $rec->{encrypted}; |
$rec->{data} = join $NULL, $name, $rec->{encrypted}; |
} |
} |
|
|
} elsif ($self->{version} == 5) { |
} |
|
elsif ($self->{version} == 5) { |
croak 'No encrypted data in record' if !defined $rec->{encrypted}; |
croak 'No encrypted data in record' if !defined $rec->{encrypted}; |
croak 'No ivec!' if !$rec->{ivec}; |
croak 'No ivec!' if !$rec->{ivec}; |
|
|
|
|
|
|
$rec->{data} = join $EMPTY, $packed, $rec->{ivec}, $rec->{encrypted}; |
$rec->{data} = join $EMPTY, $packed, $rec->{ivec}, $rec->{encrypted}; |
|
|
} else { |
} |
|
else { |
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
} |
} |
# XXX Should I? |
# XXX Should I? |
|
|
lastchange => $data->{3}->{data}, |
lastchange => $data->{3}->{data}, |
notes => $data->{255}->{data}, |
notes => $data->{255}->{data}, |
}; |
}; |
my $acctv4 = { |
my $acctv4 = {}; |
name => $acct->{0}->{data}, |
if ($acct) { |
account => $acct->{1}->{data}, |
$acctv4 = { |
password => $acct->{2}->{data}, |
name => $acct->{0}->{data}, |
lastchange => $acct->{3}->{data}, |
account => $acct->{1}->{data}, |
notes => $acct->{255}->{data}, |
password => $acct->{2}->{data}, |
}; |
lastchange => $acct->{3}->{data}, |
|
notes => $acct->{255}->{data}, |
|
}; |
|
} |
$encrypted = _encrypt_v4($datav4, $acctv4, $self->{digest}); |
$encrypted = _encrypt_v4($datav4, $acctv4, $self->{digest}); |
|
|
} elsif ($self->{version} == 5) { |
} elsif ($self->{version} == 5) { |
|
|
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
} |
} |
|
|
$rec->{plaintext}->{0} = $data->{0}; |
$rec->{plaintext} = $data; |
|
|
if ($encrypted ne '1') { |
if ($encrypted ne '1') { |
$rec->{attributes}{Dirty} = 1; |
$rec->{attributes}{Dirty} = 1; |
|
|
} |
} |
$plaintext .= chr(0xff) x 2; |
$plaintext .= chr(0xff) x 2; |
|
|
|
#print "CRYPT(e): $c->{name} [$cipher]\n"; |
my $encrypted; |
my $encrypted; |
if ($c->{name} eq 'None') { |
if ($c->{name} eq 'None') { |
# do nothing |
# do nothing |
|
|
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
} |
} |
|
|
if ($plaintext) { |
$rec->{plaintext} = $plaintext; |
$rec->{plaintext} = $plaintext; |
return $plaintext; |
return $plaintext; |
|
} |
|
return; |
|
} |
} |
|
|
sub _decrypt_v4 |
sub _decrypt_v4 |
|
|
|
|
my $modified; |
my $modified; |
if ($packed_date) { |
if ($packed_date) { |
|
#print _hexdump('DATE:', $packed_date); |
$modified = _parse_keyring_date($packed_date); |
$modified = _parse_keyring_date($packed_date); |
} |
} |
|
|
|
|
|
|
my $plaintext; |
my $plaintext; |
|
|
|
#print "CRYPT(d): $c->{name} [$cipher]\n"; |
if ($c->{name} eq 'None') { |
if ($c->{name} eq 'None') { |
# do nothing |
# do nothing |
$plaintext = $encrypted; |
$plaintext = $encrypted; |
|
|
} elsif ($c->{name} eq 'DES_EDE3' or $c->{name} eq 'Rijndael') { |
} |
|
elsif ($c->{name} eq 'DES_EDE3' or $c->{name} eq 'Rijndael') { |
require Crypt::CBC; |
require Crypt::CBC; |
my $cbc = Crypt::CBC->new( |
my $cbc = Crypt::CBC->new( |
-key => $key, |
-key => $key, |
|
|
-blocksize => $c->{blocksize}, |
-blocksize => $c->{blocksize}, |
-header => 'none', |
-header => 'none', |
-padding => 'oneandzeroes', |
-padding => 'oneandzeroes', |
) || croak("Unable to set up encryption!"); |
) || croak("Unable to set up decryption!"); |
|
|
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); |
|
|
} else { |
|
croak "Unsupported Crypt $c->{name}"; |
|
} |
} |
|
else { |
|
croak "Unsupported Crypt $c->{name} in decrypt"; |
|
} |
|
|
my %fields; |
my %fields; |
while ($plaintext) { |
while ($plaintext) { |
my $field; |
my $field; |
($field, $plaintext) = _parse_field($plaintext); |
($field, $plaintext) = _parse_field($plaintext); |
if (! $field) { |
last if ! $field; |
last; |
|
} |
|
$fields{ $field->{label_id} } = $field; |
$fields{ $field->{label_id} } = $field; |
} |
} |
|
|
|
|
if ($new_pass) { |
if ($new_pass) { |
my @accts = (); |
my @accts = (); |
foreach my $rec (@{ $self->{records} }) { |
foreach my $rec (@{ $self->{records} }) { |
my $acct = $self->Decrypt($rec, $pass); |
my $acct = $self->Decrypt($rec, $pass) |
if ( ! $acct ) { |
|| croak("Couldn't decrypt $rec->{plaintext}->{0}->{data}"); |
croak("Couldn't decrypt $rec->{plaintext}->{0}->{data}"); |
|
} |
|
push @accts, $acct; |
push @accts, $acct; |
} |
} |
|
|
if ( ! $self->_password_update($new_pass)) { |
$self->_password_update($new_pass); |
croak("Couldn't set new password!"); |
|
} |
|
$pass = $new_pass; |
$pass = $new_pass; |
|
|
foreach my $i (0..$#accts) { |
foreach my $i (0..$#accts) { |
|
|
|
|
# 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 |
$self->{digest} = _calc_keys($pass); |
$self->{digest} = _calc_keys($pass); |
$self->{password} = $pass; |
$self->{password} = $pass; |
return 1; |
|
|
|
} elsif ($self->{version} == 5) { |
return 1; |
|
} |
|
elsif ($self->{version} == 5) { |
_password_verify_v5($self->{appinfo}, $pass); |
_password_verify_v5($self->{appinfo}, $pass); |
$self->{password} = $pass; |
$self->{password} = $pass; |
return 1; |
return 1; |
|
|
my $msg = $salt . $pass; |
my $msg = $salt . $pass; |
$msg .= "\0" x ( $MD5_CBLOCK - length $msg ); |
$msg .= "\0" x ( $MD5_CBLOCK - length $msg ); |
|
|
my $digest = md5($msg); |
my $digest = md5($msg) || croak('MD5 Failed'); |
|
|
if ($data ne $salt . $digest ) { |
if ($data ne $salt . $digest ) { |
croak("Incorrect Password!"); |
croak("Incorrect Password!"); |
|
|
#print "Hash: '". $hash . "'\n"; |
#print "Hash: '". $hash . "'\n"; |
#print "Hash: '". $appinfo->{masterhash} . "'\n"; |
#print "Hash: '". $appinfo->{masterhash} . "'\n"; |
|
|
if ($appinfo->{masterhash} ne $hash) { |
if ($appinfo->{masterhash} && $appinfo->{masterhash} ne $hash) { |
croak("Incorrect Password!"); |
croak('Incorrect Password'); |
} |
} |
|
|
$appinfo->{key} = $key; |
$appinfo->{key} = $key; |
return 1; |
return 1; |
} |
} |
|
|
$self->{digest} = _calc_keys( $self->{password} ); |
$self->{digest} = _calc_keys( $self->{password} ); |
|
|
return 1; |
return 1; |
|
} |
} elsif ($self->{version} == 5) { |
elsif ($self->{version} == 5) { |
my $cipher = shift || $self->{appinfo}->{cipher}; |
my $cipher = shift || $self->{appinfo}->{cipher}; |
my $iter = shift || $self->{appinfo}->{iter}; |
my $iter = shift || $self->{appinfo}->{iter}; |
my $salt = shift || 0; |
my $salt = shift || 0; |
|
|
|
|
my $pass = shift; |
my $pass = shift; |
|
|
if (! defined $pass) { croak('No password specified!'); }; |
croak('No password specified!') if ! defined $pass; |
|
|
my $salt; |
my $salt; |
for ( 1 .. $kSalt_Size ) { |
for ( 1 .. $kSalt_Size ) { |
|
|
|
|
$msg .= "\0" x ( $MD5_CBLOCK - length $msg ); |
$msg .= "\0" x ( $MD5_CBLOCK - length $msg ); |
|
|
my $digest = md5($msg); |
my $digest = md5($msg) || croak('MD5 failed'); |
|
|
my $data = $salt . $digest; # . "\0"; |
my $data = $salt . $digest; # . "\0"; |
|
|
|
|
my $cipher = shift; |
my $cipher = shift; |
my $iter = shift; |
my $iter = shift; |
|
|
# I thought this needed to be 'blocksize', but apparently not. |
# I thought $length needed to be 'blocksize', but apparently not. |
#my $length = $CRYPTS[ $cipher ]{blocksize}; |
#my $length = $CRYPTS[ $cipher ]{blocksize}; |
my $length = 8; |
my $length = 8; |
my $salt = shift || pack("C*",map {rand(256)} 1..$length); |
my $salt = shift || pack("C*",map {rand(256)} 1..$length); |
|
|
import Digest::SHA1 qw(sha1); |
import Digest::SHA1 qw(sha1); |
|
|
my $key = _pbkdf2( $pass, $salt, $iter, $keylen, \&hmac_sha1 ); |
my $key = _pbkdf2( $pass, $salt, $iter, $keylen, \&hmac_sha1 ); |
if ($dop) { $key = _DES_odd_parity($key); } |
$key = _DES_odd_parity($key) if $dop; |
|
|
my $hash = unpack("H*", substr(sha1($key.$salt),0, 8)); |
my $hash = unpack("H*", substr(sha1($key.$salt),0, 8)); |
|
|