version 1.46, 2007/08/10 05:13:31 |
version 1.63, 2011/09/18 00:45:33 |
|
|
package Palm::Keyring; |
package Palm::Keyring; |
# $RedRiver: Keyring.pm,v 1.45 2007/02/26 00:02:13 andrew Exp $ |
# $RedRiver: Keyring.pm,v 1.61 2008/09/19 05:55:35 andrew Exp $ |
######################################################################## |
######################################################################## |
# Keyring.pm *** Perl class for Keyring for Palm OS databases. |
# Keyring.pm *** Perl class for Keyring for Palm OS databases. |
# |
# |
|
|
use strict; |
use strict; |
use warnings; |
use warnings; |
|
|
|
require 5.006_001; |
|
|
use Carp; |
use Carp; |
|
|
use base qw/ Palm::StdAppInfo /; |
use base qw/ Palm::StdAppInfo /; |
|
|
); |
); |
|
|
|
|
our $VERSION = 0.96; |
our $VERSION = '0.96_07'; |
|
|
sub new |
sub new |
{ |
{ |
|
|
else { |
else { |
$options->{password} = shift; |
$options->{password} = shift; |
$options->{version} = shift; |
$options->{version} = shift; |
|
$options->{cipher} = shift; |
} |
} |
} |
} |
|
|
|
|
$self->{appinfo}->{iter} ||= $self->{options}->{iterations}; |
$self->{appinfo}->{iter} ||= $self->{options}->{iterations}; |
}; |
}; |
|
|
|
if ( defined $options->{file} ) { |
|
$self->Load($options->{file}); |
|
} |
|
|
if ( defined $options->{password} ) { |
if ( defined $options->{password} ) { |
$self->Password($options->{password}); |
$self->Password($options->{password}); |
} |
} |
|
|
} |
} |
} |
} |
|
|
my $rc = $self->SUPER::Write(@_); |
my @rc = $self->SUPER::Write(@_); |
|
|
if ($self->{version} == 4) { |
if ($self->{version} == 4) { |
shift @{ $self->{records} }; |
shift @{ $self->{records} }; |
} |
} |
|
|
return $rc; |
return @rc; |
} |
} |
|
|
# ParseRecord |
# ParseRecord |
|
|
my $self = shift; |
my $self = shift; |
|
|
my $rec = $self->SUPER::ParseRecord(@_); |
my $rec = $self->SUPER::ParseRecord(@_); |
return $rec if ! exists $rec->{data}; |
return $rec if !(defined $rec->{data} && length $rec->{data} ); |
|
|
if ($self->{version} == 4) { |
if ($self->{version} == 4) { |
# skip the first record because it contains the password. |
# skip the first record because it contains the password. |
|
|
$self->{encpassword} = $rec->{data}; |
$self->{encpassword} = $rec->{data}; |
return '__DELETE_ME__'; |
return '__DELETE_ME__'; |
} |
} |
|
|
if ($self->{records}->[0] eq '__DELETE_ME__') { |
if ($self->{records}->[0] eq '__DELETE_ME__') { |
shift @{ $self->{records} }; |
shift @{ $self->{records} }; |
} |
} |
|
|
my ( $name, $encrypted ) = split /$NULL/xm, $rec->{data}, 2; |
my ( $name, $encrypted ) = split /$NULL/xm, $rec->{data}, 2; |
|
|
return $rec if ! $encrypted; |
return $rec if ! $encrypted; |
$rec->{decrypted}->{0} = { |
$rec->{plaintext}->{0} = { |
label => 'name', |
label => 'name', |
label_id => 0, |
label_id => 0, |
data => $name, |
data => $name, |
|
|
my ($field, $extra) = _parse_field($rec->{data}); |
my ($field, $extra) = _parse_field($rec->{data}); |
delete $rec->{data}; |
delete $rec->{data}; |
|
|
$rec->{decrypted}->{0} = $field; |
$rec->{plaintext}->{0} = $field; |
$rec->{ivec} = substr $extra, 0, $blocksize; |
$rec->{ivec} = substr $extra, 0, $blocksize; |
$rec->{encrypted} = substr $extra, $blocksize; |
$rec->{encrypted} = substr $extra, $blocksize; |
|
|
} else { |
} else { |
|
# XXX Can never get here to test, ParseAppInfoBlock is always run |
|
# XXX first by Load(). |
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
return; |
|
} |
} |
|
|
return $rec; |
return $rec; |
|
|
|
|
if ($self->{version} == 4) { |
if ($self->{version} == 4) { |
if ($rec->{encrypted}) { |
if ($rec->{encrypted}) { |
my $name = $rec->{decrypted}->{0}->{data} || $EMPTY; |
my $name = $rec->{plaintext}->{0}->{data} || $EMPTY; |
$rec->{data} = join $NULL, $name, $rec->{encrypted}; |
$rec->{data} = join $NULL, $name, $rec->{encrypted}; |
delete $rec->{decrypted}; |
|
delete $rec->{encrypted}; |
|
} |
} |
|
|
} elsif ($self->{version} == 5) { |
} elsif ($self->{version} == 5) { |
|
croak 'No encrypted data in record' if !defined $rec->{encrypted}; |
|
croak 'No ivec!' if !$rec->{ivec}; |
|
|
my $field; |
my $field; |
if ($rec->{decrypted}->{0}) { |
if ($rec->{plaintext}->{0}) { |
$field = $rec->{decrypted}->{0}; |
$field = $rec->{plaintext}->{0}; |
} else { |
} else { |
$field = { |
$field = { |
'label' => 'name', |
'label' => 'name', |
|
|
} else { |
} else { |
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
} |
} |
|
# XXX Should I? |
|
delete $rec->{plaintext}; |
|
delete $rec->{encrypted}; |
|
|
|
croak 'No data in record to pack' if !$rec->{data}; |
|
|
return $self->SUPER::PackRecord($rec, @_); |
return $self->SUPER::PackRecord($rec, @_); |
} |
} |
|
|
|
|
# Nothing extra for version 4 |
# Nothing extra for version 4 |
|
|
} elsif ($self->{version} == 5) { |
} elsif ($self->{version} == 5) { |
_parse_appinfo_v5($appinfo) || return; |
_parse_appinfo_v5($appinfo); |
|
|
} else { |
} else { |
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
|
|
{ |
{ |
my $appinfo = shift; |
my $appinfo = shift; |
|
|
if (! exists $appinfo->{other}) { |
croak 'Corrupt appinfo? no {other}' if ! $appinfo->{other}; |
# XXX Corrupt appinfo? |
|
return; |
|
} |
|
|
|
my $unpackstr |
my $unpackstr |
= ("C1" x 8) # 8 uint8s in an array for the salt |
= ("C1" x 8) # 8 uint8s in an array for the salt |
|
|
{ |
{ |
my $self = shift; |
my $self = shift; |
my $rec = shift; |
my $rec = shift; |
my $data = shift; |
|
my $pass = shift || $self->{password}; |
my $pass = shift || $self->{password}; |
|
|
|
if ( !$rec ) { |
|
croak('Needed parameter [record] not passed!'); |
|
} |
|
|
|
my $data = shift || $rec->{plaintext}; |
my $ivec = shift; |
my $ivec = shift; |
|
|
|
|
if ( ! $pass && ! $self->{appinfo}->{key}) { |
if ( ! $pass && ! $self->{appinfo}->{key}) { |
croak("password not set!\n"); |
croak('password not set!'); |
} |
} |
|
|
if ( ! $rec) { |
|
croak("Needed parameter 'record' not passed!\n"); |
|
} |
|
|
|
if ( ! $data) { |
if ( ! $data) { |
croak("Needed parameter 'data' not passed!\n"); |
croak('Needed parameter [plaintext] not passed!'); |
} |
} |
|
|
if ( $pass && ! $self->Password($pass)) { |
if ( $pass && ! $self->Password($pass)) { |
croak("Incorrect Password!\n"); |
croak('Incorrect Password!'); |
} |
} |
|
|
my $acct; |
my $acct; |
|
|
$encrypted = _encrypt_v4($datav4, $acctv4, $self->{digest}); |
$encrypted = _encrypt_v4($datav4, $acctv4, $self->{digest}); |
|
|
} elsif ($self->{version} == 5) { |
} elsif ($self->{version} == 5) { |
($encrypted, $ivec) = _encrypt_v5( |
($encrypted, $rec->{ivec}) = _encrypt_v5( |
$data, $acct, |
$data, $acct, |
$self->{appinfo}->{key}, |
$self->{appinfo}->{key}, |
$self->{appinfo}->{cipher}, |
$self->{appinfo}->{cipher}, |
$ivec, |
$ivec, |
); |
); |
if (defined $ivec) { |
|
$rec->{ivec} = $ivec; |
|
} |
|
|
|
} else { |
} else { |
croak "Unsupported Version $self->{version}"; |
croak "Unsupported version $self->{version}"; |
} |
} |
|
|
$rec->{decrypted}->{0} = $data->{0}; |
$rec->{plaintext}->{0} = $data->{0}; |
|
|
if ($encrypted) { |
if ($encrypted ne '1') { |
if ($encrypted eq '1') { |
|
return 1; |
|
} |
|
|
|
$rec->{attributes}{Dirty} = 1; |
$rec->{attributes}{Dirty} = 1; |
$rec->{attributes}{dirty} = 1; |
$rec->{attributes}{dirty} = 1; |
$rec->{encrypted} = $encrypted; |
$rec->{encrypted} = $encrypted; |
|
|
return 1; |
|
} else { |
|
return; |
|
} |
} |
|
|
|
return 1; |
} |
} |
|
|
sub _encrypt_v4 |
sub _encrypt_v4 |
|
|
my $c = crypts($cipher) or croak('Unknown cipher ' . $cipher); |
my $c = crypts($cipher) or croak('Unknown cipher ' . $cipher); |
|
|
if (! defined $ivec) { |
if (! defined $ivec) { |
$ivec = pack("C*",map {rand(256)} 1..$c->{blocksize}); |
while (! $ivec) { |
|
$ivec = pack("C*",map {rand(256)} 1..$c->{blocksize}); |
|
} |
} |
} |
|
|
my $changed = 0; |
my $changed = 0; |
|
|
} |
} |
} |
} |
|
|
return 1, 0 if $changed == 0; |
return (1, $ivec) 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]; |
|
|
year => $year, |
year => $year, |
month => $month, |
month => $month, |
day => $day, |
day => $day, |
}, |
}, |
}; |
}; |
} else { |
} else { |
# XXX Need to actually validate the above information somehow |
# XXX Need to actually validate the above information somehow |
|
|
} |
} |
} |
} |
|
|
my $decrypted; |
my $plaintext; |
foreach my $k (keys %{ $new }) { |
foreach my $k (keys %{ $new }) { |
$decrypted .= _pack_field($new->{$k}); |
next if $new->{$k}->{label_id} == 0; |
|
$plaintext .= _pack_field($new->{$k}); |
} |
} |
|
$plaintext .= chr(0xff) x 2; |
|
|
my $encrypted; |
my $encrypted; |
if ($c->{name} eq 'None') { |
if ($c->{name} eq 'None') { |
# do nothing |
# do nothing |
$encrypted = $decrypted; |
$encrypted = $plaintext; |
|
|
} 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; |
|
|
croak("Unable to set up encryption!"); |
croak("Unable to set up encryption!"); |
} |
} |
|
|
$encrypted = $cbc->encrypt($decrypted); |
$encrypted = $cbc->encrypt($plaintext); |
|
|
} else { |
} else { |
croak "Unsupported Crypt $c->{name}"; |
croak "Unsupported Crypt $c->{name}"; |
|
|
croak("No encrypted content!"); |
croak("No encrypted content!"); |
} |
} |
|
|
|
my $plaintext; |
if ($self->{version} == 4) { |
if ($self->{version} == 4) { |
$self->{digest} ||= _calc_keys( $pass ); |
$self->{digest} ||= _calc_keys( $pass ); |
my $acct = _decrypt_v4($rec->{encrypted}, $self->{digest}); |
my $acct = _decrypt_v4($rec->{encrypted}, $self->{digest}); |
return { |
$plaintext = { |
0 => $rec->{decrypted}->{0}, |
0 => $rec->{plaintext}->{0}, |
1 => { |
1 => { |
label => 'account', |
label => 'account', |
label_id => 1, |
label_id => 1, |
|
|
}; |
}; |
|
|
} elsif ($self->{version} == 5) { |
} elsif ($self->{version} == 5) { |
my $decrypted = _decrypt_v5( |
$plaintext = _decrypt_v5( |
$rec->{encrypted}, $self->{appinfo}->{key}, |
$rec->{encrypted}, $self->{appinfo}->{key}, |
$self->{appinfo}->{cipher}, $rec->{ivec}, |
$self->{appinfo}->{cipher}, $rec->{ivec}, |
); |
); |
$decrypted->{0} ||= $rec->{decrypted}->{0}; |
$plaintext->{0} ||= $rec->{plaintext}->{0}; |
return $decrypted; |
|
|
|
} else { |
} else { |
croak "Unsupported Version $self->{version}"; |
croak "Unsupported Version $self->{version}"; |
} |
} |
|
|
|
if ($plaintext) { |
|
$rec->{plaintext} = $plaintext; |
|
return $plaintext; |
|
} |
return; |
return; |
} |
} |
|
|
|
|
my $encrypted = shift; |
my $encrypted = shift; |
my $digest = shift; |
my $digest = shift; |
|
|
my $decrypted = _crypt3des( $encrypted, $digest, $DECRYPT ); |
my $plaintext = _crypt3des( $encrypted, $digest, $DECRYPT ); |
my ( $account, $password, $notes, $packed_date ) |
my ( $account, $password, $notes, $packed_date ) |
= split /$NULL/xm, $decrypted, 4; |
= split /$NULL/xm, $plaintext, 4; |
|
|
my $modified; |
my $modified; |
if ($packed_date) { |
if ($packed_date) { |
|
|
|
|
my $c = crypts($cipher) or croak('Unknown cipher ' . $cipher); |
my $c = crypts($cipher) or croak('Unknown cipher ' . $cipher); |
|
|
my $decrypted; |
my $plaintext; |
|
|
if ($c->{name} eq 'None') { |
if ($c->{name} eq 'None') { |
# do nothing |
# do nothing |
$decrypted = $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 $len = $c->{blocksize} - length($encrypted) % $c->{blocksize}; |
my $len = $c->{blocksize} - length($encrypted) % $c->{blocksize}; |
$encrypted .= $NULL x $len; |
$encrypted .= $NULL x $len; |
$decrypted = $cbc->decrypt($encrypted); |
$plaintext = $cbc->decrypt($encrypted); |
|
|
} else { |
} else { |
croak "Unsupported Crypt $c->{name}"; |
croak "Unsupported Crypt $c->{name}"; |
} |
} |
|
|
my %fields; |
my %fields; |
while ($decrypted) { |
while ($plaintext) { |
my $field; |
my $field; |
($field, $decrypted) = _parse_field($decrypted); |
($field, $plaintext) = _parse_field($plaintext); |
if (! $field) { |
if (! $field) { |
last; |
last; |
} |
} |
|
|
foreach my $rec (@{ $self->{records} }) { |
foreach my $rec (@{ $self->{records} }) { |
my $acct = $self->Decrypt($rec, $pass); |
my $acct = $self->Decrypt($rec, $pass); |
if ( ! $acct ) { |
if ( ! $acct ) { |
croak("Couldn't decrypt $rec->{decrypted}->{0}->{data}"); |
croak("Couldn't decrypt $rec->{plaintext}->{0}->{data}"); |
} |
} |
push @accts, $acct; |
push @accts, $acct; |
} |
} |
|
|
|
|
foreach my $i (0..$#accts) { |
foreach my $i (0..$#accts) { |
delete $self->{records}->[$i]->{encrypted}; |
delete $self->{records}->[$i]->{encrypted}; |
$self->Encrypt($self->{records}->[$i], $accts[$i], $pass); |
$self->{records}->[$i]->{plaintext} = $accts[$i]; |
|
$self->Encrypt($self->{records}->[$i], $pass); |
} |
} |
} |
} |
|
|
|
|
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; |
|
|
return $key; |
return $key; |
} |
} |
|
|
|
sub Unlock |
|
{ |
|
my $self = shift; |
|
my ($pass) = @_; |
|
$pass ||= $self->{password}; |
|
|
|
if ( $pass && ! $self->Password($pass)) { |
|
croak("Invalid Password!\n"); |
|
} |
|
|
|
foreach my $rec (@{ $self->{records} }) { |
|
$self->Decrypt($rec); |
|
} |
|
|
|
return 1; |
|
|
|
} |
|
|
|
sub Lock |
|
{ |
|
my $self = shift; |
|
|
|
$self->Password(); |
|
|
|
foreach my $rec (@{ $self->{records} }) { |
|
my $name = $rec->{plaintext}->{0}; |
|
delete $rec->{plaintext}; |
|
$rec->{plaintext}->{0} = $name; |
|
} |
|
|
|
return 1; |
|
} |
|
|
# Helpers |
# Helpers |
|
|
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!'); }; |
|
|
|
|
|
|
my ($len) = unpack "n", $field; |
my ($len) = unpack "n", $field; |
if ($len + 4 > length $field) { |
if ($len + 4 > length $field) { |
return undef, $field; |
return (undef, $field); |
} |
} |
my $unpackstr = "x2 C1 C1 A$len"; |
my $unpackstr = "x2 C1 C1 A$len"; |
my $offset = 2 +1 +1 +$len; |
my $offset = 2 +1 +1 +$len; |
|
|
my $maxlines = shift; # Max # of lines to dump |
my $maxlines = shift; # Max # of lines to dump |
my $offset; # Offset of current chunk |
my $offset; # Offset of current chunk |
|
|
|
my @lines; |
|
|
for ($offset = 0; $offset < length($data); $offset += 16) |
for ($offset = 0; $offset < length($data); $offset += 16) |
{ |
{ |
my $hex; # Hex values of the data |
my $hex; # Hex values of the data |
|
|
|
|
($ascii = $chunk) =~ y/\040-\176/./c; |
($ascii = $chunk) =~ y/\040-\176/./c; |
|
|
printf "%s %-48s|%-16s|\n", $prefix, $hex, $ascii; |
push @lines, sprintf "%s %-48s|%-16s|\n", $prefix, $hex, $ascii; |
} |
} |
|
return wantarray ? @lines : \@lines; |
} |
} |
|
|
sub _bindump |
sub _bindump |
|
|
my $maxlines = shift; # Max # of lines to dump |
my $maxlines = shift; # Max # of lines to dump |
my $offset; # Offset of current chunk |
my $offset; # Offset of current chunk |
|
|
|
my @lines; |
|
|
for ($offset = 0; $offset < length($data); $offset += 8) |
for ($offset = 0; $offset < length($data); $offset += 8) |
{ |
{ |
my $bin; # binary values of the data |
my $bin; # binary values of the data |
|
|
|
|
($ascii = $chunk) =~ y/\040-\176/./c; |
($ascii = $chunk) =~ y/\040-\176/./c; |
|
|
printf "%s %-72s|%-8s|\n", $prefix, $bin, $ascii; |
push @lines, sprintf "%s %-72s|%-8s|\n", $prefix, $bin, $ascii; |
} |
} |
|
return wantarray ? @lines : \@lines; |
} |
} |
|
|
# Thanks to Jochen Hoenicke <hoenicke@gmail.com> |
# Thanks to Jochen Hoenicke <hoenicke@gmail.com> |
|
|
# keylen is length of generated key in bytes |
# keylen is length of generated key in bytes |
# prf is the pseudo random function (e.g. hmac_sha1) |
# prf is the pseudo random function (e.g. hmac_sha1) |
# returns the key. |
# returns the key. |
sub _pbkdf2($$$$$) |
sub _pbkdf2 |
{ |
{ |
my ($password, $salt, $iter, $keylen, $prf) = @_; |
my ($password, $salt, $iter, $keylen, $prf) = @_; |
my ($k, $t, $u, $ui, $i); |
my ($k, $t, $u, $ui, $i); |
|
|
return substr($t, 0, $keylen); |
return substr($t, 0, $keylen); |
} |
} |
|
|
sub _DES_odd_parity($) { |
sub _DES_odd_parity { |
my $key = $_[0]; |
my $key = $_[0]; |
my ($r, $i); |
my ($r, $i); |
my @odd_parity = ( |
my @odd_parity = ( |
|
|
parses Keyring for Palm OS databases. See |
parses Keyring for Palm OS databases. See |
L<http://gnukeyring.sourceforge.net/>. |
L<http://gnukeyring.sourceforge.net/>. |
|
|
It has the standard Palm::PDB methods with 2 additional public methods. |
It has the standard Palm::PDB methods with 4 additional public methods. |
Decrypt and Encrypt. |
Unlock, Lock, Decrypt and Encrypt. |
|
|
It currently supports the v4 Keyring databases as well as |
It currently supports the v4 Keyring databases as well as |
the pre-release v5 databases. I am not completely happy with the interface |
the pre-release v5 databases. |
for accessing v5 databases, so any suggestions on improvements on |
|
the interface are appreciated. |
|
|
|
This module doesn't store the decrypted content. It only keeps it until it |
|
returns it to you or encrypts it. |
|
|
|
=head1 SYNOPSIS |
=head1 SYNOPSIS |
|
|
use Palm::PDB; |
use Palm::PDB; |
|
|
my $pdb = new Palm::PDB; |
my $pdb = new Palm::PDB; |
$pdb->Load($file); |
$pdb->Load($file); |
|
|
|
$pdb->Unlock($pass); |
foreach my $rec (@{ $pdb->{records} }) { |
foreach my $rec (@{ $pdb->{records} }) { |
my $acct = $pdb->Decrypt($rec, $pass); |
print $rec->{plaintext}->{0}->{data}, ' - ', |
print $acct->{0}->{data}, ' - ', $acct->{1}->{data}, "\n"; |
$rec->{plaintext}->{1}->{data}, "\n"; |
} |
} |
|
$pdb->Lock(); |
|
|
=head1 SUBROUTINES/METHODS |
=head1 SUBROUTINES/METHODS |
|
|
=head2 new |
=head2 new |
|
|
$pdb = new Palm::Keyring([$password[, $version]]); |
$pdb = new Palm::Keyring([$password[, $version[, $cipher]]]); |
|
|
Create a new PDB, initialized with the various Palm::Keyring fields |
Create a new PDB, initialized with the various Palm::Keyring fields |
and an empty record list. |
and an empty record list. |
|
|
Use this method if you're creating a Keyring PDB from scratch otherwise you |
Use this method if you're creating a Keyring PDB from scratch otherwise you |
can just use Palm::PDB::new() before calling Load(). |
can just use Palm::PDB::new() before calling Load(). |
|
|
If you pass in a password, it will initalize the first record with the encrypted |
If you pass in a password, it will initalize the database with the encrypted |
password. |
password. |
|
|
new() now also takes options in other formats |
new() now also takes options in other formats |
|
|
|
|
=item cipher |
=item cipher |
|
|
The cipher to use. Either the number or the name. |
The cipher to use. Either the number or the name. Only used by v5 datbases. |
|
|
0 => None |
0 => None |
1 => DES_EDE3 |
1 => DES_EDE3 |
|
|
|
|
=item iterations |
=item iterations |
|
|
The number of iterations to encrypt with. |
The number of iterations to encrypt with. Only used by somy crypts in v5 databases. |
|
|
=item options |
=item file |
|
|
A hashref of the options that are set |
The name of a file to Load(). This will override many of the other options. |
|
|
=back |
=back |
|
|
|
|
|
|
=head2 labels |
=head2 labels |
|
|
Pass in the id or the name of the label; |
Pass in the id or the name of the label. The label id is used as a key |
|
to the different parts of the records. |
|
See Encrypt() for details on where the label is used. |
|
|
This is a function, not a method. |
This is a function, not a method. |
|
|
|
|
|
|
=head2 Encrypt |
=head2 Encrypt |
|
|
$pdb->Encrypt($rec, $acct[, $password[, $ivec]]); |
=head3 B<!!! IMPORTANT !!!> The order of the arguments to Encrypt has |
|
changed. $password and $plaintext used to be swapped. They changed |
|
because you can now set $rec->{plaintext} and not pass in $plaintext so |
|
$password is more important. |
|
|
|
$pdb->Encrypt($rec[, $password[, $plaintext[, $ivec]]]); |
|
|
Encrypts an account into a record, either with the password previously |
Encrypts an account into a record, either with the password previously |
used, or with a password that is passed. |
used, or with a password that is passed. |
|
|
|
|
randomly. |
randomly. |
|
|
$rec is a record from $pdb->{records} or a new_Record(). |
$rec is a record from $pdb->{records} or a new_Record(). |
The $acct is a hashref in the format below. |
$rec->{plaintext} is a hashref in the format below. |
|
|
my $acct = { |
$plaintext = { |
0 => { |
0 => { |
label => 'name', |
label => 'name', |
label_id => 0, |
label_id => 0, |
|
|
label => 'lastchange', |
label => 'lastchange', |
label_id => 3, |
label_id => 3, |
font => 0, |
font => 0, |
data => $lastchange, |
data => { |
|
year => $year, # usually the year - 1900 |
|
mon => $mon, # range 0-11 |
|
day => $day, # range 1-31 |
|
}, |
}, |
}, |
255 => { |
255 => { |
label => 'notes', |
label => 'notes', |
|
|
}, |
}, |
}; |
}; |
|
|
The account name is also stored in $rec->{decrypted}->{0}->{data} for both v4 |
The account name is stored in $rec->{plaintext}->{0}->{data} for both v4 |
and v5 databases. |
and v5 databases even when the record has not been Decrypt()ed. |
|
|
$rec->{decrypted}->{0} => { |
$rec->{plaintext}->{0} => { |
label => 'name', |
label => 'name', |
data => 'account name', |
label_id => 0, |
|
font => 0, |
|
data => 'account name', |
}; |
}; |
|
|
If you have changed anything other than the lastchange, or don't pass in a |
If you have changed anything other than the lastchange, or don't pass in a |
|
|
If you pass in a lastchange field that is different than the one in the |
If you pass in a lastchange field that is different than the one in the |
record, it will honor what you passed in. |
record, it will honor what you passed in. |
|
|
|
You can either set $rec->{plaintext} or pass in $plaintext. $plaintext |
|
is used over anything in $rec->{plaintext}. |
|
|
|
|
=head2 Decrypt |
=head2 Decrypt |
|
|
my $acct = $pdb->Decrypt($rec[, $password]); |
my $plaintext = $pdb->Decrypt($rec[, $password]); |
|
|
Decrypts the record and returns a reference for the account as described |
Decrypts the record and returns a reference for the plaintext account as |
under Encrypt(). |
described under Encrypt(). |
|
Also sets $rec->{plaintext} with the same information as $plaintext as |
|
described in Encrypt(). |
|
|
foreach my $rec (@{ $pdb->{records} }) { |
foreach my $rec (@{ $pdb->{records} }) { |
my $acct = $pdb->Decrypt($rec); |
my $plaintext = $pdb->Decrypt($rec); |
# do something with $acct |
# do something with $plaintext |
} |
} |
|
|
|
|
|
|
or calculated when setting a new password. |
or calculated when setting a new password. |
}; |
}; |
|
|
=head2 Other overridden subroutines/methods |
=head2 Unlock |
|
|
=over |
$pdb->Unlock([$password]); |
|
|
=item Write |
Decrypts all the records. Sets $rec->{plaintext} for all records. |
|
|
For v4 databases it also puts back the record 0 for the encrypted password |
This makes it easy to show all decrypted information. |
before writing it. |
|
|
|
|
my $pdb = Palm::KeyRing->new(); |
|
$pdb->Load($keyring_file); |
|
$pdb->Unlock($password); |
|
foreach my $plaintext (map { $_->{plaintext} } @{ $pdb->{records} }) { |
|
# Do something like display the account. |
|
} |
|
$pdb->Lock(); |
|
|
|
=head2 Lock |
|
|
|
$pdb->Lock(); |
|
|
|
Unsets $rec->{plaintext} for all records and unsets the saved password. |
|
|
|
This does NOT Encrypt() any of the records before clearing them, so if |
|
you are not careful you will lose information. |
|
|
|
B<CAVEAT!> This only does "delete $rec->{plaintext}" and the same for the |
|
password. If someone knows of a cross platform reliable way to make |
|
sure that the information is actually cleared from memory I would |
|
appreciate it. Also, if someone knows how to make sure that the stuff |
|
in $rec->{plaintext} is not written to swap, that would be very handy as |
|
well. |
|
|
|
=head2 Other overridden subroutines/methods |
|
|
|
=over |
|
|
=item ParseAppInfoBlock |
=item ParseAppInfoBlock |
|
|
Converts the extra returned by Palm::StdAppInfo::ParseAppInfoBlock() into |
Converts the extra returned by Palm::StdAppInfo::ParseAppInfoBlock() into |
|
|
|
|
Reverses ParseRecord and then sends it through Palm::StdAppInfo::PackRecord() |
Reverses ParseRecord and then sends it through Palm::StdAppInfo::PackRecord() |
|
|
|
=item Write |
|
|
|
For v4 databases it puts back the record 0 for the encrypted password before |
|
writing it. |
|
|
=back |
=back |
|
|
=head1 DEPENDENCIES |
=head1 DEPENDENCIES |
|
|
|
|
=head1 THANKS |
=head1 THANKS |
|
|
I would like to thank the helpful Perlmonk shigetsu who gave me some great advice |
I would like to thank the helpful Perlmonk shigetsu who gave me some great |
and helped me get my first module posted. L<http://perlmonks.org/?node_id=596998> |
advice and helped me get my first module posted. |
|
L<http://perlmonks.org/?node_id=596998> |
|
|
I would also like to thank |
I would also like to thank |
Johan Vromans |
Johan Vromans |
|
|
I am not sure I am 'require module' the best way, but I don't want to |
I am not sure I am 'require module' the best way, but I don't want to |
depend on modules that you don't need to use. |
depend on modules that you don't need to use. |
|
|
I am not very happy with the data structures used by Encrypt() and |
|
Decrypt() for v5 databases, but I am not sure of a better way. |
|
|
|
The v4 compatibility mode does not insert a fake record 0 where |
|
normally the encrypted password is stored. |
|
|
|
The date validation for packing new dates is very poor. |
The date validation for packing new dates is very poor. |
|
|
I have not gone through and standardized on how the module fails. Some |
I have not gone through and standardized on how the module fails. Some |
things fail with croak, some return undef, some may even fail silently. |
things fail with croak, some return undef, some may even fail silently. |
Nothing initializes a lasterr method or anything like that. I need |
Nothing initializes a lasterr method or anything like that. |
to fix all that before it is a 1.0 candidate. |
|
|
This module does not do anything special with the plaintext data. It SHOULD |
|
treat it somehow special so that it can't be found in RAM or in a swap file |
|
anywhere. I don't have a clue how to do this. |
|
|
|
I need to fix all this before it is a 1.0 candidate. |
|
|
Please report any bugs or feature requests to |
Please report any bugs or feature requests to |
C<bug-palm-keyring at rt.cpan.org>, or through the web interface at |
C<bug-palm-keyring at rt.cpan.org>, or through the web interface at |