=================================================================== RCS file: /cvs/palm/Palm-Keyring/lib/Palm/Keyring.pm,v retrieving revision 1.12 retrieving revision 1.27 diff -u -r1.12 -r1.27 --- palm/Palm-Keyring/lib/Palm/Keyring.pm 2007/01/28 00:18:46 1.12 +++ palm/Palm-Keyring/lib/Palm/Keyring.pm 2007/02/10 16:21:28 1.27 @@ -1,458 +1,608 @@ -# Palm::Keyring.pm +package Palm::Keyring; +# $RedRiver: Keyring.pm,v 1.26 2007/02/06 02:58:50 andrew Exp $ +######################################################################## +# Keyring.pm *** Perl class for Keyring for Palm OS databases. # -# Perl class for dealing with Keyring for Palm OS databases. -# -# Copyright (C) 2004, Andrew Fresh -# You may distribute this file under the terms of the Artistic -# License, as specified in the README file distributed with the p5-Palm distribution. -# # This started as Memo.pm, I just made it work for Keyring. # -# $RedRiver: Keyring.pm,v 1.11 2007/01/27 23:59:29 andrew Exp $ - +# 2006.01.26 #*#*# andrew fresh +######################################################################## +# Copyright (C) 2006, 2007 by Andrew Fresh +# +# This program is free software; you can redistribute it and/or modify +# it under the same terms as Perl itself. +######################################################################## use strict; -package Palm::Keyring; -use Palm::Raw(); -use Palm::StdAppInfo(); -use vars qw( $VERSION @ISA ); +use warnings; +use Carp; + +use base qw/ Palm::StdAppInfo /; + use Digest::MD5 qw(md5); use Crypt::DES; -use constant ENCRYPT => 1; -use constant DECRYPT => 0; -use constant MD5_CBLOCK => 64; -my $kSaltSize = 4; +my $ENCRYPT = 1; +my $DECRYPT = 0; +my $MD5_CBLOCK = 64; +my $kSalt_Size = 4; +my $EMPTY = q{}; +my $SPACE = q{ }; +my $NULL = chr 0; +our $VERSION = 0.94; -# One liner, to allow MakeMaker to work. -$VERSION = do { my @r = (q$Revision: 1.12 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; +sub new { + my $classname = shift; + my $pass = shift; -@ISA = qw( Palm::StdAppInfo Palm::Raw ); + # Create a generic PDB. No need to rebless it, though. + my $self = $classname->SUPER::new(@_); -=head1 NAME + $self->{'name'} = 'Keys-Gtkr'; # Default + $self->{'creator'} = 'Gtkr'; + $self->{'type'} = 'Gkyr'; -Palm::Keyring - Handler for Palm Keyring databases. + # The PDB is not a resource database by + # default, but it's worth emphasizing, + # since MemoDB is explicitly not a PRC. + $self->{'attributes'}{'resource'} = 0; -=head1 SYNOPSIS + # Initialize the AppInfo block + $self->{'appinfo'} = {}; - use Palm::PDB; - use Palm::Keyring; - my $pdb = new Palm::PDB; - $pdb->Load($file); - foreach my $record (@{ $pdb->{'records'} }) { - print "$record->{'plaintext'}->{'name'}\n"; - } - $pdb->Decrypt($password); - # do something with the decrypted parts + # Add the standard AppInfo block stuff + Palm::StdAppInfo::seed_StdAppInfo( $self->{'appinfo'} ); -=head1 DESCRIPTION + # Set the version + $self->{'version'} = 4; -The Keyring PDB handler is a helper class for the Palm::PDB package. It -parses Keyring for Palm OS databases. See -L. + if ( defined $pass ) { + $self->Password($pass); + } -It has the standard Palm::PDB methods with 2 additional public methods. -Decrypt and Encrypt. + return $self; +} -It currently supports the v4 Keyring databases. The v5 databases from the pre-release keyring-2.0 are not supported. +sub import { + Palm::PDB::RegisterPDBHandlers( __PACKAGE__, [ 'Gtkr', 'Gkyr' ], ); + return 1; +} -=cut +sub ParseRecord { + my $self = shift; -=head2 new + my $rec = $self->SUPER::ParseRecord(@_); - $pdb = new Palm::Keyring([$password]); + # skip the 0 record that holds the password + return $rec if ! exists $self->{'records'}; + return $rec if ! exists $rec->{'data'}; -Create a new PDB, initialized with the various Palm::Keyring fields -and an empty record list. + my ( $name, $encrypted ) = split /$NULL/xm, $rec->{'data'}, 2; -Use this method if you're creating a Keyring PDB from scratch otherwise you -can just use Palm::PDB::new(). + return $rec if ! $encrypted; + delete $rec->{'data'}; + $rec->{'name'} = $name; + $rec->{'encrypted'} = $encrypted; -=cut + return $rec; +} -sub new -{ - my $classname = shift; - my $pass = shift; +sub PackRecord { + my $self = shift; + my $rec = shift; - # Create a generic PDB. No need to rebless it, though. - my $self = $classname->SUPER::new(@_); + if ($rec->{'encrypted'}) { + if (! defined $rec->{'name'}) { + $rec->{'name'} = $EMPTY; + } + $rec->{'data'} = join $NULL, $rec->{'name'}, $rec->{'encrypted'}; + delete $rec->{'name'}; + delete $rec->{'encrypted'}; + } - $self->{name} = "Keys-Gtkr"; # Default - $self->{creator} = "Gtkr"; - $self->{type} = "Gkyr"; - # The PDB is not a resource database by - # default, but it's worth emphasizing, - # since MemoDB is explicitly not a PRC. - $self->{attributes}{resource} = 0; + return $self->SUPER::PackRecord($rec, @_); +} - # Initialize the AppInfo block - $self->{appinfo} = {}; +sub Encrypt { + my $self = shift; + my $rec = shift; + my $data = shift; + my $pass = shift || $self->{'password'}; - # Add the standard AppInfo block stuff - &Palm::StdAppInfo::seed_StdAppInfo($self->{appinfo}); + if ( ! $pass) { + croak("'password' not set!\n"); + } - # Set the version - $self->{version} = 4; + if ( ! $rec) { + croak("Needed parameter 'record' not passed!\n"); + } - # Give the PDB the first record that will hold the encrypted password - $self->{records} = [ $self->new_Record ]; + if ( ! $data) { + croak("Needed parameter 'data' not passed!\n"); + } - if (defined $pass) { - $self->Encrypt($pass); - } + if ( ! $self->Password($pass)) { + croak("Incorrect Password!\n"); + } - return $self; -} + $self->{'digest'} ||= _calc_keys( $pass ); -sub import -{ - &Palm::PDB::RegisterPDBHandlers(__PACKAGE__, - [ "Gtkr", "Gkyr" ], - ); -} + $data->{'account'} ||= $EMPTY; + $data->{'password'} ||= $EMPTY; + $data->{'notes'} ||= $EMPTY; -=pod + my $changed = 0; + my $need_newdate = 0; + my $acct = {}; + if ($rec->{'encrypted'}) { + $acct = $self->Decrypt($rec, $pass); + foreach my $key (keys %{ $data }) { + next if $key eq 'lastchange'; + if ($data->{$key} ne $acct->{$key}) { + $changed = 1; + last; + } + } + if ( exists $data->{'lastchange'} && exists $acct->{'lastchange'} && ( + $data->{'lastchange'}->{day} != $acct->{'lastchange'}->{day} || + $data->{'lastchange'}->{month} != $acct->{'lastchange'}->{month} || + $data->{'lastchange'}->{year} != $acct->{'lastchange'}->{year} + )) { + $changed = 1; + $need_newdate = 0; + } else { + $need_newdate = 1; + } -=head2 Load + } else { + $changed = 1; + } - $pdb->Load($filename[, $password]); + # no need to re-encrypt if it has not changed. + return 1 if ! $changed; -Overrides the standard Palm::Raw Load() to add -$record->{'plaintext'}->{'name'} and -$record->{'encrypted'} fields. -$record->{'plaintext'}->{'name'} holds the name of the record, -$record->{'encrypted'} is the encrypted information in the PDB. + my ($day, $month, $year); -It also takes an additional optional parameter, which is the password to use to -decrypt the database. + if ($data->{'lastchange'} && ! $need_newdate ) { + $day = $data->{'lastchange'}->{'day'} || 1; + $month = $data->{'lastchange'}->{'month'} || 0; + $year = $data->{'lastchange'}->{'year'} || 0; -See Decrypt() for the additional fields that are available after decryption. + # XXX Need to actually validate the above information somehow + if ($year >= 1900) { + $year -= 1900; + } + } else { + $need_newdate = 1; + } -=cut + if ($need_newdate) { + ($day, $month, $year) = (localtime)[3,4,5]; + } + $year -= 4; + $month++; -sub Load -{ - my $self = shift; - my $filename = shift; - my $password = shift; - $self->{'appinfo'} = {}; - $self->{'records'} = []; - $self->SUPER::Load($filename); + my $p = $day | ($month << 5) | ($year << 9); + my $packeddate = pack 'n', $p; - foreach my $record (@{ $self->{records} }) { - next unless exists $record->{data}; - my ($name, $encrypted) = split /\000/, $record->{data}, 2; - next unless $encrypted; - $record->{plaintext}->{name} = $name; - $record->{encrypted} = $encrypted; - } + my $plaintext = join $NULL, + $data->{'account'}, $data->{'password'}, $data->{'notes'}, $packeddate; - return $self->Decrypt($password) if defined $password; + my $encrypted = _crypt3des( $plaintext, $self->{'digest'}, $ENCRYPT ); - 1; + return if ! $encrypted; + + $rec->{'attributes'}{'Dirty'} = 1; + $rec->{'attributes'}{'dirty'} = 1; + $rec->{'name'} ||= $data->{'name'}; + $rec->{'encrypted'} = $encrypted; + + return 1; } -=pod +sub Decrypt { + my $self = shift; + my $rec = shift; + my $pass = shift || $self->{'password'}; -=head2 Write + if ( ! $pass) { + croak("'password' not set!\n"); + } - $pdb->Write($filename[, $password]); + if ( ! $rec) { + croak("Needed parameter 'record' not passed!\n"); + } -Just like the Palm::Raw::Write() but encrypts everything before saving. + if ( ! $self->Password($pass)) { + croak("Invalid Password!\n"); + } -Also takes an optional password to encrypt with a new password, not needed -unless you are changing the password. + if ( ! $rec->{'encrypted'} ) { + croak("No encrypted content!"); + } -=cut + $self->{'digest'} ||= _calc_keys( $pass ); -sub Write -{ - my $self = shift; - my $filename = shift; - my $password = shift; + my $decrypted = + _crypt3des( $rec->{'encrypted'}, $self->{'digest'}, $DECRYPT ); + my ( $account, $password, $notes, $packeddate ) = split /$NULL/xm, + $decrypted, 4; - $self->Encrypt($password) || return undef; - return $self->SUPER::Write($filename); + my %Modified; + if ($packeddate) { + my $u = unpack 'n', $packeddate; + my $year = (($u & 0xFE00) >> 9) + 4; # since 1900 + my $month = (($u & 0x01E0) >> 5) - 1; # 0-11 + my $day = (($u & 0x001F) >> 0); # 1-31 + + %Modified = ( + year => $year, + month => $month || 0, + day => $day || 1, + ); + } + + return { + name => $rec->{'name'}, + account => $account, + password => $password, + notes => $notes, + lastchange => \%Modified, + }; } -=pod +sub Password { + my $self = shift; + my $pass = shift; + my $new_pass = shift; -=head2 Encrypt + if (! $pass) { + delete $self->{password}; + return 1; + } - $pdb->Encrypt([$password]); + if (! exists $self->{'records'}) { + # Give the PDB the first record that will hold the encrypted password + $self->{'records'} = [ $self->new_Record ]; -Encrypts the PDB, either with the password used to decrypt or create it, or -optionally with a password that is passed. + return $self->_password_update($pass); + } -See Decrypt() for an what plaintext fields are available to be encrypted. + if ($new_pass) { + my @accts = (); + foreach my $i (0..$#{ $self->{'records'} }) { + if ($i == 0) { + push @accts, undef; + next; + } + my $acct = $self->Decrypt($self->{'records'}->[$i], $pass); + if ( ! $acct ) { + croak("Couldn't decrypt $self->{'records'}->[$i]->{'name'}"); + } + push @accts, $acct; + } -=cut + if ( ! $self->_password_update($new_pass)) { + croak("Couldn't set new password!"); + } + $pass = $new_pass; -sub Encrypt -{ - my $self = shift; - my $pass = shift; + foreach my $i (0..$#accts) { + next if $i == 0; + delete $self->{'records'}->[$i]->{'encrypted'}; + $self->Encrypt($self->{'records'}->[$i], $accts[$i], $pass); + } + } - if ($pass) { - unless (exists $self->{'records'}->[0]->{'data'} && - $self->_keyring_verify($pass) ) { - # This would encrypt with a new password. - # First decrypting everything with the old password of course. - $self->_keyring_update($pass) || return undef; - $self->_keyring_verify($pass) || return undef; - } - } + return $self->_password_verify($pass); +} - $self->{digest} ||= _calc_keys($self->{password}); +sub _calc_keys { + my $pass = shift; + if (! defined $pass) { croak('No password defined!'); }; - foreach my $record (@{ $self->{records} }) { - next unless defined $record->{plaintext}; + my $digest = md5($pass); - my $name = defined $record->{plaintext}->{name} ? - $record->{plaintext}->{name} : ''; - my $account = defined $record->{plaintext}->{account} ? - $record->{plaintext}->{account} : ''; - my $password = defined $record->{plaintext}->{password} ? - $record->{plaintext}->{password} : ''; - my $description = defined $record->{plaintext}->{description} ? - $record->{plaintext}->{description} : ''; - my $extra = ''; + my ( $key1, $key2 ) = unpack 'a8a8', $digest; - my $plaintext = join("\000", $account, $password, $description, $extra); + #-------------------------------------------------- + # print "key1: $key1: ", length $key1, "\n"; + # print "key2: $key2: ", length $key2, "\n"; + #-------------------------------------------------- - my $encrypted = _crypt3des($plaintext, $self->{digest}, ENCRYPT); + $digest = unpack 'H*', $key1 . $key2 . $key1; - $record->{data} = join("\000", $name, $encrypted); - } + #-------------------------------------------------- + # print "Digest: ", $digest, "\n"; + # print length $digest, "\n"; + #-------------------------------------------------- - 1; + return $digest; } -=head2 Decrypt +sub _password_verify { + my $self = shift; + my $pass = shift; - $pdb->Decrypt([$password]); + if (! $pass) { croak('No password specified!'); }; -Decrypts the PDB and fills out the rest of the fields available in -$record->{'plaintext'}. + if (defined $self->{'password'} && $pass eq $self->{'password'}) { + # already verified this password + return 1; + } -The plaintext should now be this, before encryption or after decryption: + # AFAIK the thing we use to test the password is + # always in the first entry + my $data = $self->{'records'}->[0]->{'data'}; - $record->{'plaintext'} = { - name => $name, - account => $account, - password => $account_password, - description => $description, - }; + #die "No encrypted password in file!" unless defined $data; + if ( ! defined $data) { return; }; -=cut + $data =~ s/$NULL$//xm; -sub Decrypt -{ - my $self = shift; - my $pass = shift; + my $salt = substr $data, 0, $kSalt_Size; - if ($pass) { - $self->_keyring_verify($pass) || return undef; - } + my $msg = $salt . $pass; - $self->{digest} ||= _calc_keys($self->{password}); + $msg .= "\0" x ( $MD5_CBLOCK - length $msg ); - foreach my $record (@{ $self->{records} }) { - next unless defined $record->{data}; + my $digest = md5($msg); - my ($name, $encrypted) = split /\000/, $record->{data}, 2; - next unless $encrypted; + if ( $data eq $salt . $digest ) { - $record->{plaintext}->{name} = $name; +# May as well generate the keys we need now, since we know the password is right + $self->{'digest'} = _calc_keys($pass); + if ( $self->{'digest'} ) { + $self->{'password'} = $pass; + return 1; + } + } + return; +} - my $decrypted = _crypt3des($encrypted, $self->{digest}, DECRYPT); - my ($account, $password, $description, $extra) - = split /\000/, $decrypted, 4; +sub _password_update { - $record->{plaintext}->{account} = defined $account ? - $account : ''; - $record->{plaintext}->{password} = defined $password ? - $password : ''; - $record->{plaintext}->{description} = defined $description ? - $description : ''; + # It is very important to Encrypt after calling this + # (Although it is generally only called by Encrypt) + # because otherwise the data will be out of sync with the + # password, and that would suck! + my $self = shift; + my $pass = shift; - #print "Name: '$name'\n"; - #print "Encrypted: '$encrypted' - Length: " . length($encrypted) . "\n"; - #print " Hex: '" . unpack("H*", $encrypted) . "'\n"; - #print " Binary:'" . unpack("b*", $encrypted) . "'\n"; - #print "Decrypted: '$decrypted' - Length: " . length($decrypted) . "\n"; - #print " Hex: '" . unpack("H*", $decrypted) . "'\n"; - #print " Binary:'" . unpack("b*", $decrypted) . "'\n"; - #print "\n"; - #print "Extra: $extra\n"; - #exit; - #-------------------------------------------------- - # print "Account: $account\n"; - # print "Password: $password\n"; - # print "Description: $description\n"; - #-------------------------------------------------- + if (! defined $pass) { croak('No password specified!'); }; - } + my $salt; + for ( 1 .. $kSalt_Size ) { + $salt .= chr int rand 255; + } - 1; -} + my $msg = $salt . $pass; -sub _calc_keys -{ - my $pass = shift; - die "No password defined!" unless defined $pass; + $msg .= "\0" x ( $MD5_CBLOCK - length $msg ); - my $digest = md5($pass); + my $digest = md5($msg); - my ($key1, $key2) = unpack('a8a8', $digest); - #-------------------------------------------------- - # print "key1: $key1: ", length $key1, "\n"; - # print "key2: $key2: ", length $key2, "\n"; - #-------------------------------------------------- + my $data = $salt . $digest; # . "\0"; - $digest = unpack('H*', $key1 . $key2 . $key1); - #-------------------------------------------------- - # print "Digest: ", $digest, "\n"; - # print length $digest, "\n"; - #-------------------------------------------------- + # AFAIK the thing we use to test the password is + # always in the first entry + $self->{'records'}->[0]->{'data'} = $data; - return $digest; + $self->{'password'} = $pass; + $self->{'digest'} = _calc_keys( $self->{'password'} ); + + return 1; } -sub _keyring_verify -{ - my $self = shift; - my $pass = shift; +sub _crypt3des { + my ( $plaintext, $passphrase, $flag ) = @_; - die "No password specified!" unless $pass; + $passphrase .= $SPACE x ( 16 * 3 ); + my $cyphertext = $EMPTY; - # AFAIK the thing we use to test the password is - # always in the first entry - my $data = $self->{records}->[0]->{data}; - #die "No encrypted password in file!" unless defined $data; - return undef unless defined $data; + my $size = length $plaintext; - $data =~ s/\0$//; + #print "STRING: '$plaintext' - Length: " . (length $plaintext) . "\n"; - my $salt = substr($data, 0, $kSaltSize); + my @C; + for ( 0 .. 2 ) { + $C[$_] = + new Crypt::DES( pack 'H*', ( substr $passphrase, 16 * $_, 16 )); + } - my $msg = $salt . $pass; + for ( 0 .. ( ($size) / 8 ) ) { + my $pt = substr $plaintext, $_ * 8, 8; - $msg .= "\0" x (MD5_CBLOCK - length($msg)); + #print "PT: '$pt' - Length: " . length($pt) . "\n"; + if (! length $pt) { next; }; + if ( (length $pt) < 8 ) { + if ($flag == $DECRYPT) { croak('record not 8 byte padded'); }; + my $len = 8 - (length $pt); - my $digest = md5($msg); + #print "LENGTH: $len\n"; + #print "Binary: '" . unpack("b*", $pt) . "'\n"; + $pt .= ($NULL x $len); - if ($data eq $salt . $digest) { - # May as well generate the keys we need now, since we know the password is right - $self->{digest} = _calc_keys($pass); - if ($self->{digest}) { - $self->{password} = $pass; - return 1; - } else { - return undef; - } - } else { - return undef; - } + #print "PT: '$pt' - Length: " . length($pt) . "\n"; + #print "Binary: '" . unpack("b*", $pt) . "'\n"; + } + if ( $flag == $ENCRYPT ) { + $pt = $C[0]->encrypt($pt); + $pt = $C[1]->decrypt($pt); + $pt = $C[2]->encrypt($pt); + } + else { + $pt = $C[0]->decrypt($pt); + $pt = $C[1]->encrypt($pt); + $pt = $C[2]->decrypt($pt); + } + + #print "PT: '$pt' - Length: " . length($pt) . "\n"; + $cyphertext .= $pt; + } + + $cyphertext =~ s/$NULL+$//xm; + + #print "CT: '$cyphertext' - Length: " . length($cyphertext) . "\n"; + + return $cyphertext; } -sub _keyring_update -{ - # It is very important to Encrypt after calling this - # (Although it is generally only called by Encrypt) - # because otherwise the data will be out of sync with the - # password, and that would suck! - my $self = shift; - my $pass = shift; +1; +__END__ - die "No password specified!" unless $pass; +=head1 NAME - # if the database already has a password in it - if ($self->{records}->[0]->{data}) { - # Make sure everything is decrypted before we update the keyring - $self->Decrypt() || return undef; - } +Palm::Keyring - Handler for Palm Keyring databases. - my $salt; - for (1..$kSaltSize) { - $salt .= chr(int(rand(255))); - } +=head1 DESCRIPTION - my $msg = $salt . $pass; +The Keyring PDB handler is a helper class for the Palm::PDB package. It +parses Keyring for Palm OS databases. See +L. - $msg .= "\0" x (MD5_CBLOCK - length($msg)); +It has the standard Palm::PDB methods with 2 additional public methods. +Decrypt and Encrypt. - my $digest = md5($msg); +It currently supports the v4 Keyring databases. The v5 databases from +the pre-release keyring-2.0 are not supported. - my $data = $salt . $digest;# . "\0"; +This module doesn't store the decrypted content. It only keeps it until it +returns it to you or encrypts it. - # AFAIK the thing we use to test the password is - # always in the first entry - $self->{records}->[0]->{data} = $data; +=head1 SYNOPSIS - $self->{password} = $pass; - $self->{digest} = _calc_keys($self->{password}); + use Palm::PDB; + use Palm::Keyring; + + my $pass = 'password'; + my $file = 'Keys-Gtkr.pdb'; + my $pdb = new Palm::PDB; + $pdb->Load($file); + + foreach (0..$#{ $pdb->{'records'} }) { + next if $_ = 0; # skip the password record + my $rec = $pdb->{'records'}->[$_]; + my $acct = $pdb->Decrypt($rec, $pass); + print $rec->{'name'}, ' - ', $acct->{'account'}, "\n"; + } - return 1; -} +=head1 SUBROUTINES/METHODS -sub _crypt3des { - my ( $plaintext, $passphrase, $flag ) = @_; - my $NULL = chr(0); +=head2 new - $passphrase .= ' ' x (16*3); - my $cyphertext = ""; + $pdb = new Palm::Keyring([$password]); - my $size = length ( $plaintext ); - #print "STRING: '$plaintext' - Length: " . length($plaintext) . "\n"; +Create a new PDB, initialized with the various Palm::Keyring fields +and an empty record list. - my @C; - for ( 0..2 ) { - $C[$_] = new Crypt::DES( pack( "H*", substr($passphrase, 16*$_, 16 ))); - } +Use this method if you're creating a Keyring PDB from scratch otherwise you +can just use Palm::PDB::new() before calling Load(). - for ( 0 .. (($size)/8)) { - my $pt = substr( $plaintext, $_*8, 8 ); - #print "PT: '$pt' - Length: " . length($pt) . "\n"; - next unless length($pt); - if (length($pt) < 8) { - die "record not 8 byte padded" if $flag == DECRYPT; - my $len = 8 - length($pt); - #print "LENGTH: $len\n"; - #print "Binary: '" . unpack("b*", $pt) . "'\n"; - $pt .= ($NULL x $len); - #print "PT: '$pt' - Length: " . length($pt) . "\n"; - #print "Binary: '" . unpack("b*", $pt) . "'\n"; - } - if ($flag == ENCRYPT) { - $pt = $C[0]->encrypt( $pt ); - $pt = $C[1]->decrypt( $pt ); - $pt = $C[2]->encrypt( $pt ); - } else { - $pt = $C[0]->decrypt( $pt ); - $pt = $C[1]->encrypt( $pt ); - $pt = $C[2]->decrypt( $pt ); - } - #print "PT: '$pt' - Length: " . length($pt) . "\n"; - $cyphertext .= $pt; - } +If you pass in a password, it will initalize the first record with the encrypted +password. - $cyphertext =~ s/$NULL+$//; - #print "CT: '$cyphertext' - Length: " . length($cyphertext) . "\n"; +=head2 Encrypt - return $cyphertext; -} + $pdb->Encrypt($rec, $acct[, $password]); -1; -__END__ +Encrypts an account into a record, either with the password previously +used, or with a password that is passed. +$rec is a record from $pdb->{'records'} or a new_Record(). +$acct is a hashref in the format below. + + my $acct = { + name => $rec->{'name'}, + account => $account, + password => $password, + notes => $notes, + lastchange => { + year => 107, # years since 1900 + month => 0, # 0-11, 0 = January, 11 = December + day => 30, # 1-31, same as localtime + }, + }; + +If you have changed anything other than the lastchange, or don't pass in a +lastchange key, Encrypt() will generate a new lastchange date for you. + +If you pass in a lastchange field that is different than the one in the +record, it will honor what you passed in. + +Encrypt() only uses the $acct->{'name'} if there is not already a $rec->{'name'}. + +=head2 Decrypt + + my $acct = $pdb->Decrypt($rec[, $password]); + +Decrypts the record and returns a hashref for the account as described +under Encrypt(). + + foreach (0..$#{ $pdb->{'records'}) { + next if $_ == 0; + my $rec = $pdb->{'records'}->[$_]; + my $acct = $pdb->Decrypt($rec[, $password]); + # do something with $acct + } + +=head2 Password + + $pdb->Password([$password[, $new_password]]); + +Either sets the password to be used to crypt, or if you pass $new_password, +changes the password on the database. + +If you have created a new $pdb, and you didn't set a password when you +called new(), you only need to pass one password and it will set that as +the password. + +If nothing is passed, it forgets the password that it was remembering. + +=head1 DEPENDENCIES + +Palm::StdAppInfo + +Digest::MD5 + +Crypt::DES + +Readonly + +=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 also like to thank +Johan Vromans +Ejvromans@squirrel.nlE -- +L. +He had his own Palm::KeyRing module that he posted a couple of days before +mine was ready and he was kind enough to let me have the namespace as well +as giving me some very helpful hints about doing a few things that I was +unsure of. He is really great. + +=head1 BUGS AND LIMITATIONS + +Please report any bugs or feature requests to +C, or through the web interface at +L. I will be notified, and then you'll automatically be +notified of progress on your bug as I make changes. + =head1 AUTHOR -Andrew Fresh Eandrew@mad-techies.orgE +Andrew Fresh Eandrew@cpan.orgE +=head1 LICENSE AND COPYRIGHT + +Copyright 2004, 2005, 2006, 2007 Andrew Fresh, All Rights Reserved. + +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + =head1 SEE ALSO Palm::PDB(3) @@ -462,4 +612,5 @@ The Keyring for Palm OS website: L -=cut +Johan Vromans also has a wxkeyring app that now uses this module, available +from his website at L