===================================================================
RCS file: /cvs/palm/Palm-Keyring/lib/Palm/Keyring.pm,v
retrieving revision 1.46
retrieving revision 1.53
diff -u -r1.46 -r1.53
--- palm/Palm-Keyring/lib/Palm/Keyring.pm 2007/08/10 05:13:31 1.46
+++ palm/Palm-Keyring/lib/Palm/Keyring.pm 2007/12/04 03:34:17 1.53
@@ -1,5 +1,5 @@
package Palm::Keyring;
-# $RedRiver: Keyring.pm,v 1.45 2007/02/26 00:02:13 andrew Exp $
+# $RedRiver: Keyring.pm,v 1.52 2007/12/04 03:33:34 andrew Exp $
########################################################################
# Keyring.pm *** Perl class for Keyring for Palm OS databases.
#
@@ -83,7 +83,7 @@
);
-our $VERSION = 0.96;
+our $VERSION = '0.96_06';
sub new
{
@@ -249,7 +249,7 @@
my ( $name, $encrypted ) = split /$NULL/xm, $rec->{data}, 2;
return $rec if ! $encrypted;
- $rec->{decrypted}->{0} = {
+ $rec->{plaintext}->{0} = {
label => 'name',
label_id => 0,
data => $name,
@@ -265,7 +265,7 @@
my ($field, $extra) = _parse_field($rec->{data});
delete $rec->{data};
- $rec->{decrypted}->{0} = $field;
+ $rec->{plaintext}->{0} = $field;
$rec->{ivec} = substr $extra, 0, $blocksize;
$rec->{encrypted} = substr $extra, $blocksize;
@@ -286,16 +286,16 @@
if ($self->{version} == 4) {
if ($rec->{encrypted}) {
- my $name = $rec->{decrypted}->{0}->{data} || $EMPTY;
+ my $name = $rec->{plaintext}->{0}->{data} || $EMPTY;
$rec->{data} = join $NULL, $name, $rec->{encrypted};
- delete $rec->{decrypted};
+ delete $rec->{plaintext};
delete $rec->{encrypted};
}
} elsif ($self->{version} == 5) {
my $field;
- if ($rec->{decrypted}->{0}) {
- $field = $rec->{decrypted}->{0};
+ if ($rec->{plaintext}->{0}) {
+ $field = $rec->{plaintext}->{0};
} else {
$field = {
'label' => 'name',
@@ -422,8 +422,8 @@
{
my $self = shift;
my $rec = shift;
- my $data = shift;
my $pass = shift || $self->{password};
+ my $data = shift || $rec->{plaintext};
my $ivec = shift;
if ( ! $pass && ! $self->{appinfo}->{key}) {
@@ -435,7 +435,7 @@
}
if ( ! $data) {
- croak("Needed parameter 'data' not passed!\n");
+ croak("Needed 'plaintext' not passed!\n");
}
if ( $pass && ! $self->Password($pass)) {
@@ -481,7 +481,7 @@
croak "Unsupported Version $self->{version}";
}
- $rec->{decrypted}->{0} = $data->{0};
+ $rec->{plaintext}->{0} = $data->{0};
if ($encrypted) {
if ($encrypted eq '1') {
@@ -620,7 +620,7 @@
year => $year,
month => $month,
day => $day,
- },
+ },
};
} else {
# XXX Need to actually validate the above information somehow
@@ -629,15 +629,17 @@
}
}
- my $decrypted;
+ my $plaintext;
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;
if ($c->{name} eq 'None') {
# do nothing
- $encrypted = $decrypted;
+ $encrypted = $plaintext;
} elsif ($c->{name} eq 'DES_EDE3' or $c->{name} eq 'Rijndael') {
require Crypt::CBC;
@@ -656,7 +658,7 @@
croak("Unable to set up encryption!");
}
- $encrypted = $cbc->encrypt($decrypted);
+ $encrypted = $cbc->encrypt($plaintext);
} else {
croak "Unsupported Crypt $c->{name}";
@@ -689,11 +691,12 @@
croak("No encrypted content!");
}
+ my $plaintext;
if ($self->{version} == 4) {
$self->{digest} ||= _calc_keys( $pass );
my $acct = _decrypt_v4($rec->{encrypted}, $self->{digest});
- return {
- 0 => $rec->{decrypted}->{0},
+ $plaintext = {
+ 0 => $rec->{plaintext}->{0},
1 => {
label => 'account',
label_id => 1,
@@ -721,16 +724,20 @@
};
} elsif ($self->{version} == 5) {
- my $decrypted = _decrypt_v5(
+ $plaintext = _decrypt_v5(
$rec->{encrypted}, $self->{appinfo}->{key},
$self->{appinfo}->{cipher}, $rec->{ivec},
);
- $decrypted->{0} ||= $rec->{decrypted}->{0};
- return $decrypted;
+ $plaintext->{0} ||= $rec->{plaintext}->{0};
} else {
croak "Unsupported Version $self->{version}";
}
+
+ if ($plaintext) {
+ $rec->{plaintext} = $plaintext;
+ return $plaintext;
+ }
return;
}
@@ -739,9 +746,9 @@
my $encrypted = shift;
my $digest = shift;
- my $decrypted = _crypt3des( $encrypted, $digest, $DECRYPT );
+ my $plaintext = _crypt3des( $encrypted, $digest, $DECRYPT );
my ( $account, $password, $notes, $packed_date )
- = split /$NULL/xm, $decrypted, 4;
+ = split /$NULL/xm, $plaintext, 4;
my $modified;
if ($packed_date) {
@@ -766,11 +773,11 @@
my $c = crypts($cipher) or croak('Unknown cipher ' . $cipher);
- my $decrypted;
+ my $plaintext;
if ($c->{name} eq 'None') {
# do nothing
- $decrypted = $encrypted;
+ $plaintext = $encrypted;
} elsif ($c->{name} eq 'DES_EDE3' or $c->{name} eq 'Rijndael') {
require Crypt::CBC;
@@ -790,16 +797,16 @@
}
my $len = $c->{blocksize} - length($encrypted) % $c->{blocksize};
$encrypted .= $NULL x $len;
- $decrypted = $cbc->decrypt($encrypted);
+ $plaintext = $cbc->decrypt($encrypted);
} else {
croak "Unsupported Crypt $c->{name}";
}
my %fields;
- while ($decrypted) {
+ while ($plaintext) {
my $field;
- ($field, $decrypted) = _parse_field($decrypted);
+ ($field, $plaintext) = _parse_field($plaintext);
if (! $field) {
last;
}
@@ -835,7 +842,7 @@
foreach my $rec (@{ $self->{records} }) {
my $acct = $self->Decrypt($rec, $pass);
if ( ! $acct ) {
- croak("Couldn't decrypt $rec->{decrypted}->{0}->{data}");
+ croak("Couldn't decrypt $rec->{plaintext}->{0}->{data}");
}
push @accts, $acct;
}
@@ -847,7 +854,8 @@
foreach my $i (0..$#accts) {
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);
}
}
@@ -1037,6 +1045,39 @@
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
sub _calc_keys
@@ -1352,17 +1393,12 @@
parses Keyring for Palm OS databases. See
L.
-It has the standard Palm::PDB methods with 2 additional public methods.
-Decrypt and Encrypt.
+It has the standard Palm::PDB methods with 4 additional public methods.
+Unlock, Lock, Decrypt and Encrypt.
It currently supports the v4 Keyring databases as well as
-the pre-release v5 databases. I am not completely happy with the interface
-for accessing v5 databases, so any suggestions on improvements on
-the interface are appreciated.
+the pre-release v5 databases.
-This module doesn't store the decrypted content. It only keeps it until it
-returns it to you or encrypts it.
-
=head1 SYNOPSIS
use Palm::PDB;
@@ -1373,10 +1409,12 @@
my $pdb = new Palm::PDB;
$pdb->Load($file);
+ $pdb->Unlock($pass);
foreach my $rec (@{ $pdb->{records} }) {
- my $acct = $pdb->Decrypt($rec, $pass);
- print $acct->{0}->{data}, ' - ', $acct->{1}->{data}, "\n";
+ print $rec->{plaintext}->{0}->{data}, ' - ',
+ $rec->{plaintext}->{1}->{data}, "\n";
}
+ $pdb->Lock();
=head1 SUBROUTINES/METHODS
@@ -1390,7 +1428,7 @@
Use this method if you're creating a Keyring PDB from scratch otherwise you
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.
new() now also takes options in other formats
@@ -1414,7 +1452,7 @@
=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
1 => DES_EDE3
@@ -1423,12 +1461,8 @@
=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
-
-A hashref of the options that are set
-
=back
=back
@@ -1468,7 +1502,9 @@
=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.
@@ -1492,8 +1528,13 @@
=head2 Encrypt
- $pdb->Encrypt($rec, $acct[, $password[, $ivec]]);
+=head3 B 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
used, or with a password that is passed.
@@ -1502,9 +1543,9 @@
randomly.
$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 => {
label => 'name',
label_id => 0,
@@ -1526,7 +1567,11 @@
label => 'lastchange',
label_id => 3,
font => 0,
- data => $lastchange,
+ data => {
+ year => $year, # usually the year - 1900
+ mon => $mon, # range 0-11
+ day => $day, # range 1-31
+ },
},
255 => {
label => 'notes',
@@ -1536,12 +1581,14 @@
},
};
-The account name is also stored in $rec->{decrypted}->{0}->{data} for both v4
-and v5 databases.
+The account name is stored in $rec->{plaintext}->{0}->{data} for both v4
+and v5 databases even when the record has not been Decrypt()ed.
- $rec->{decrypted}->{0} => {
- label => 'name',
- data => 'account name',
+ $rec->{plaintext}->{0} => {
+ label => 'name',
+ label_id => 0,
+ font => 0,
+ data => 'account name',
};
If you have changed anything other than the lastchange, or don't pass in a
@@ -1550,17 +1597,22 @@
If you pass in a lastchange field that is different than the one in the
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
- my $acct = $pdb->Decrypt($rec[, $password]);
+ my $plaintext = $pdb->Decrypt($rec[, $password]);
-Decrypts the record and returns a reference for the account as described
-under Encrypt().
+Decrypts the record and returns a reference for the plaintext account as
+described under Encrypt().
+Also sets $rec->{plaintext} with the same information as $plaintext as
+described in Encrypt().
foreach my $rec (@{ $pdb->{records} }) {
- my $acct = $pdb->Decrypt($rec);
- # do something with $acct
+ my $plaintext = $pdb->Decrypt($rec);
+ # do something with $plaintext
}
@@ -1600,15 +1652,42 @@
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
-before writing it.
+This makes it easy to show all decrypted information.
+ 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 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
Converts the extra returned by Palm::StdAppInfo::ParseAppInfoBlock() into
@@ -1641,6 +1720,11 @@
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
=head1 DEPENDENCIES
@@ -1669,8 +1753,9 @@
=head1 THANKS
-I would like to thank the helpful Perlmonk shigetsu who gave me some great advice
-and helped me get my first module posted. L
+I would like to thank the helpful Perlmonk shigetsu who gave me some great
+advice and helped me get my first module posted.
+L
I would also like to thank
Johan Vromans
@@ -1695,18 +1780,17 @@
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.
-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.
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.
-Nothing initializes a lasterr method or anything like that. I need
-to fix all that before it is a 1.0 candidate.
+Nothing initializes a lasterr method or anything like that.
+
+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
C, or through the web interface at