[BACK]Return to Keyring.pm CVS log [TXT][DIR] Up to [local] / palm / Palm-Keyring / lib / Palm

Diff for /palm/Palm-Keyring/lib/Palm/Keyring.pm between version 1.46 and 1.48

version 1.46, 2007/08/10 05:13:31 version 1.48, 2007/09/12 03:44:36
Line 1 
Line 1 
 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.47 2007/09/12 00:30:10 andrew Exp $
 ########################################################################  ########################################################################
 # Keyring.pm *** Perl class for Keyring for Palm OS databases.  # Keyring.pm *** Perl class for Keyring for Palm OS databases.
 #  #
Line 83 
Line 83 
 );  );
   
   
 our $VERSION = 0.96;  our $VERSION = '0.96_01';
   
 sub new  sub new
 {  {
Line 249 
Line 249 
         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,
Line 265 
Line 265 
         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;
   
Line 286 
Line 286 
   
     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->{plaintext};
             delete $rec->{encrypted};              delete $rec->{encrypted};
         }          }
   
     } elsif ($self->{version} == 5) {      } elsif ($self->{version} == 5) {
         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',
Line 422 
Line 422 
 {  {
     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};
       my $data = shift || $rec->{plaintext};
     my $ivec = shift;      my $ivec = shift;
   
     if ( ! $pass && ! $self->{appinfo}->{key}) {      if ( ! $pass && ! $self->{appinfo}->{key}) {
Line 435 
Line 435 
     }      }
   
     if ( ! $data) {      if ( ! $data) {
         croak("Needed parameter 'data' not passed!\n");          croak("Needed 'plaintext' not passed!\n");
     }      }
   
     if ( $pass && ! $self->Password($pass)) {      if ( $pass && ! $self->Password($pass)) {
Line 481 
Line 481 
         croak "Unsupported Version $self->{version}";          croak "Unsupported Version $self->{version}";
     }      }
   
     $rec->{decrypted}->{0} = $data->{0};      $rec->{plaintext}->{0} = $data->{0};
   
     if ($encrypted) {      if ($encrypted) {
         if ($encrypted eq '1') {          if ($encrypted eq '1') {
Line 620 
Line 620 
                 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
Line 629 
Line 629 
         }          }
     }      }
   
     my $decrypted;      my $plaintext;
     foreach my $k (keys %{ $new }) {      foreach my $k (keys %{ $new }) {
         $decrypted .= _pack_field($new->{$k});          $plaintext .= _pack_field($new->{$k});
     }      }
   
     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;
Line 656 
Line 656 
             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}";
Line 689 
Line 689 
         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,
Line 721 
Line 722 
         };          };
   
     } 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;
 }  }
   
Line 739 
Line 744 
     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) {
Line 766 
Line 771 
   
     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;
Line 790 
Line 795 
         }          }
         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;
         }          }
Line 835 
Line 840 
         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;
         }          }
Line 847 
Line 852 
   
         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);
         }          }
     }      }
   
Line 1037 
Line 1043 
     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
Line 1360 
Line 1399 
 for accessing v5 databases, so any suggestions on improvements on  for accessing v5 databases, so any suggestions on improvements on
 the interface are appreciated.  the interface are appreciated.
   
 This module doesn't store the decrypted content.  It only keeps it until it  This module doesn't store the plaintext content.  It only keeps it until it
 returns it to you or encrypts it.  returns it to you or encrypts it.
   
 =head1 SYNOPSIS  =head1 SYNOPSIS
Line 1374 
Line 1413 
     $pdb->Load($file);      $pdb->Load($file);
   
     foreach my $rec (@{ $pdb->{records} }) {      foreach my $rec (@{ $pdb->{records} }) {
         my $acct = $pdb->Decrypt($rec, $pass);          my $plaintext = $pdb->Decrypt($rec, $pass);
         print $acct->{0}->{data}, ' - ', $acct->{1}->{data}, "\n";          print $plaintext->{0}->{data}, ' - ', $plaintext->{1}->{data}, "\n";
     }      }
   
 =head1 SUBROUTINES/METHODS  =head1 SUBROUTINES/METHODS
Line 1492 
Line 1531 
   
 =head2 Encrypt  =head2 Encrypt
   
     $pdb->Encrypt($rec, $acct[, $password[, $ivec]]);      $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.
Line 1502 
Line 1541 
 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,
Line 1536 
Line 1575 
         },          },
     };      };
   
 The account name is also stored in $rec->{decrypted}->{0}->{data} for both v4  The account name is also stored in $rec->{plaintext}->{0}->{data} for both v4
 and v5 databases.  and v5 databases.
   
     $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
Line 1550 
Line 1591 
 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 L<Encrypt>.
   Also sets $rec->{plaintext} with the same information as $plaintext as
   described in L<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
     }      }
   
   
Line 1600 
Line 1646 
                       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 L<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
Line 1641 
Line 1714 
   
 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
Line 1669 
Line 1747 
   
 =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
Line 1697 
Line 1776 
   
 I am not very happy with the data structures used by Encrypt() and  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.  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.
   

Legend:
Removed from v.1.46  
changed lines
  Added in v.1.48

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>