[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.31 and 1.35

version 1.31, 2007/02/19 02:55:35 version 1.35, 2007/02/22 04:11:35
Line 1 
Line 1 
 package Palm::Keyring;  package Palm::Keyring;
 # $RedRiver: Keyring.pm,v 1.30 2007/02/19 01:37:10 andrew Exp $  # $RedRiver: Keyring.pm,v 1.34 2007/02/21 05:24:14 andrew Exp $
 ########################################################################  ########################################################################
 # Keyring.pm *** Perl class for Keyring for Palm OS databases.  # Keyring.pm *** Perl class for Keyring for Palm OS databases.
 #  #
Line 16 
Line 16 
 use warnings;  use warnings;
   
 use Carp;  use Carp;
 use Data::Dumper;  $Carp::Verbose = 1;
   
 use base qw/ Palm::StdAppInfo /;  use base qw/ Palm::StdAppInfo /;
   
 use Digest::HMAC_SHA1 qw(hmac_sha1);  
 use Digest::SHA1 qw(sha1);  
 use Crypt::CBC;  
   
 use Digest::MD5 qw(md5);  
 use Crypt::DES;  
   
 my $ENCRYPT    = 1;  my $ENCRYPT    = 1;
 my $DECRYPT    = 0;  my $DECRYPT    = 0;
 my $MD5_CBLOCK = 64;  my $MD5_CBLOCK = 64;
Line 36 
Line 29 
 my $NULL       = chr 0;  my $NULL       = chr 0;
   
 my @CRYPTS = (  my @CRYPTS = (
     { # None      {
           alias     => 'None',
         name      => 'None',          name      => 'None',
         keylen    => 8,          keylen    => 8,
         blocksize => 1,          blocksize => 1,
         default_iter => 500,          default_iter => 500,
     },      },
     { # DES-EDE3      {
           alias     => 'DES-EDE3',
         name      => 'DES_EDE3',          name      => 'DES_EDE3',
         keylen    => 24,          keylen    => 24,
         blocksize =>  8,          blocksize =>  8,
         DES_odd_parity => 1,          DES_odd_parity => 1,
         default_iter => 1000,          default_iter => 1000,
     },      },
     { # AES128      {
           alias     => 'AES128',
         name      => 'Rijndael',          name      => 'Rijndael',
         keylen    => 16,          keylen    => 16,
         blocksize => 16,          blocksize => 16,
         default_iter => 100,          default_iter => 100,
     },      },
     { # AES256      {
           alias     => 'AES256',
         name      => 'Rijndael',          name      => 'Rijndael',
         keylen    => 32,          keylen    => 32,
         blocksize => 16,          blocksize => 16,
Line 131 
Line 128 
     return 1;      return 1;
 }  }
   
   # Accessors
   
   sub crypts
   {
       my $crypt = shift;
       if ($crypt =~ /\D/) {
           foreach my $c (@CRYPTS) {
               if ($c->{alias} eq $crypt) {
                   return $c;
               }
           }
           # didn't find it.
           return;
       } else {
           return $CRYPTS[$crypt];
       }
   }
   
 # ParseRecord  # ParseRecord
   
 sub ParseRecord  sub ParseRecord
Line 160 
Line 175 
         $rec->{name}      = $field->{data};          $rec->{name}      = $field->{data};
         $rec->{ivec}      = $ivec;          $rec->{ivec}      = $ivec;
         $rec->{encrypted} = $encrypted;          $rec->{encrypted} = $encrypted;
           delete $rec->{data};
   
     } else {      } else {
         die 'Unsupported Version';          die 'Unsupported Version';
Line 247 
Line 263 
   
     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
         . ("S1" x 2)  # the iter (uint16) and the cipher (uint16)          . ("n1" x 2)  # the iter (uint16) and the cipher (uint16)
         . ("C1" x 8); # and finally 8 more uint8s for the hash          . ("C1" x 8); # and finally 8 more uint8s for the hash
   
     my (@salt, $iter, $cipher, @hash);      my (@salt, $iter, $cipher, @hash);
Line 288 
Line 304 
   
     my $packstr      my $packstr
         = ("C1" x 8)  # 8 uint8s in an array for the salt          = ("C1" x 8)  # 8 uint8s in an array for the salt
         . ("S1" x 2)  # the iter (uint16) and the cipher (uint16)          . ("n1" x 2)  # the iter (uint16) and the cipher (uint16)
         . ("C1" x 8); # and finally 8 more uint8s for the hash          . ("C1" x 8); # and finally 8 more uint8s for the hash
   
     my @salt = map { hex $_ } $appinfo->{salt} =~ /../gxm;      my @salt = map { hex $_ } $appinfo->{salt} =~ /../gxm;
Line 314 
Line 330 
     my $rec  = shift;      my $rec  = shift;
     my $data = shift;      my $data = shift;
     my $pass = shift || $self->{password};      my $pass = shift || $self->{password};
       my $ivec = shift;
   
     if ( ! $pass && ! $self->{appinfo}->{key}) {      if ( ! $pass && ! $self->{appinfo}->{key}) {
         croak("password not set!\n");          croak("password not set!\n");
Line 343 
Line 360 
         $rec->{name}    ||= $data->{name};          $rec->{name}    ||= $data->{name};
   
     } elsif ($self->{version} == 5) {      } elsif ($self->{version} == 5) {
         my @recs = ($data, $acct);          my @accts = ($data, $acct);
         my $name;  
         if ($self->{options}->{v4compatible}) {          if ($self->{options}->{v4compatible}) {
             $rec->{name} ||= $data->{name};              $rec->{name} ||= $data->{name};
             foreach my $rec (@recs) {              foreach my $a (@accts) {
                 my @fields;                  my @fields;
                 foreach my $k (sort keys %{ $rec }) {                  foreach my $k (sort keys %{ $a }) {
                     my $field = {                      my $field = {
                         label    => $k,                          label    => $k,
                         font     => 0,                          font     => 0,
                         data     => $rec->{$k},                          data     => $a->{$k},
                     };                      };
                     push @fields, $field;                      push @fields, $field;
                 }                  }
                 $rec = \@fields;                  $a = \@fields;
             }              }
         }          }
   
         my $ivec;  
         ($encrypted, $ivec) = _encrypt_v5(          ($encrypted, $ivec) = _encrypt_v5(
             @recs,              @accts,
             $self->{appinfo}->{key},              $self->{appinfo}->{key},
             $self->{appinfo}->{cipher},              $self->{appinfo}->{cipher},
               $ivec,
         );          );
         if ($ivec) {          if (defined $ivec) {
             $rec->{ivec} = $ivec;              $rec->{ivec} = $ivec;
         }          }
   
Line 465 
Line 481 
     my $old    = shift;      my $old    = shift;
     my $key    = shift;      my $key    = shift;
     my $cipher = shift;      my $cipher = shift;
     my $length = $CRYPTS[ $cipher ]{blocksize};      my $ivec   = shift;
     my $ivec   = shift || pack("C*",map {rand(256)} 1..$length);      my $blocksize   = $CRYPTS[ $cipher ]{blocksize};
   
     my $keylen      = $CRYPTS[ $cipher ]{keylen};      my $keylen      = $CRYPTS[ $cipher ]{keylen};
     my $cipher_name = $CRYPTS[ $cipher ]{name};      my $cipher_name = $CRYPTS[ $cipher ]{name};
   
       if (! defined $ivec) {
           $ivec = pack("C*",map {rand(256)} 1..$blocksize);
       }
   
     my $changed = 0;      my $changed = 0;
     my $need_newdate = 1;      my $need_newdate = 1;
     my $date_index;      my $date_index;
Line 533 
Line 552 
     foreach my $field (@{ $new }) {      foreach my $field (@{ $new }) {
         $decrypted .= _pack_field($field);          $decrypted .= _pack_field($field);
     }      }
   
     my $encrypted;      my $encrypted;
     if ($cipher_name eq 'None') {      if ($cipher_name eq 'None') {
         # do nothing          # do nothing
         $encrypted = $decrypted;          $encrypted = $decrypted;
   
     } elsif ($cipher_name eq 'DES_EDE3' or $cipher_name eq 'Rijndael') {      } elsif ($cipher_name eq 'DES_EDE3' or $cipher_name eq 'Rijndael') {
           require Crypt::CBC;
         my $c = Crypt::CBC->new(          my $c = Crypt::CBC->new(
             -literal_key => 1,  
             -key         => $key,              -key         => $key,
               -literal_key => 1,
             -iv          => $ivec,              -iv          => $ivec,
             -cipher      => $cipher_name,              -cipher      => $cipher_name,
             -keysize     => $keylen,              -keysize     => $keylen,
               -blocksize   => $blocksize,
             -header      => 'none',              -header      => 'none',
             -padding     => 'oneandzeroes',              -padding     => 'oneandzeroes',
         );          );
Line 639 
Line 659 
   
 sub _decrypt_v5  sub _decrypt_v5
 {  {
   
     my $encrypted = shift;      my $encrypted = shift;
     my $key       = shift;      my $key       = shift;
     my $cipher    = shift;      my $cipher    = shift;
Line 646 
Line 667 
   
     my $keylen       = $CRYPTS[ $cipher ]{keylen};      my $keylen       = $CRYPTS[ $cipher ]{keylen};
     my $cipher_name  = $CRYPTS[ $cipher ]{name};      my $cipher_name  = $CRYPTS[ $cipher ]{name};
       my $blocksize    = $CRYPTS[ $cipher ]{blocksize};
   
     my $decrypted;      my $decrypted;
   
Line 654 
Line 676 
         $decrypted = $encrypted;          $decrypted = $encrypted;
   
     } elsif ($cipher_name eq 'DES_EDE3' or $cipher_name eq 'Rijndael') {      } elsif ($cipher_name eq 'DES_EDE3' or $cipher_name eq 'Rijndael') {
           require Crypt::CBC;
         my $c = Crypt::CBC->new(          my $c = Crypt::CBC->new(
             -literal_key => 1,  
             -key         => $key,              -key         => $key,
               -literal_key => 1,
             -iv          => $ivec,              -iv          => $ivec,
             -cipher      => $cipher_name,              -cipher      => $cipher_name,
             -keysize     => $keylen,              -keysize     => $keylen,
               -blocksize   => $blocksize,
             -header      => 'none',              -header      => 'none',
             -padding     => 'oneandzeroes',              -padding     => 'oneandzeroes',
         );          );
Line 667 
Line 691 
         if (! $c) {          if (! $c) {
             croak("Unable to set up encryption!");              croak("Unable to set up encryption!");
         }          }
         $encrypted .= $NULL x $keylen; # pad out a keylen          my $len = $blocksize - length($encrypted) % $blocksize;
           $encrypted .= $NULL x $len;
         $decrypted  = $c->decrypt($encrypted);          $decrypted  = $c->decrypt($encrypted);
   
     } else {      } else {
Line 766 
Line 791 
             }              }
         }          }
     } elsif ($self->{version} == 5) {      } elsif ($self->{version} == 5) {
         return _password_verify_v5($pass, $self->{appinfo});          return _password_verify_v5($self->{appinfo}, $pass);
     } else {      } else {
         # XXX unsupported version          # XXX unsupported version
     }      }
Line 776 
Line 801 
   
 sub _password_verify_v4  sub _password_verify_v4
 {  {
       require Digest::MD5;
       import Digest::MD5 qw(md5);
   
     my $pass = shift;      my $pass = shift;
     my $data = shift;      my $data = shift;
   
Line 793 
Line 821 
   
     my $digest = md5($msg);      my $digest = md5($msg);
   
     if (! $data eq $salt . $digest ) {      if ($data ne $salt . $digest ) {
         return;          return;
     }      }
   
Line 802 
Line 830 
   
 sub _password_verify_v5  sub _password_verify_v5
 {  {
     my $pass    = shift;  
     my $appinfo = shift;      my $appinfo = shift;
       my $pass    = shift;
   
     my $salt = pack("H*", $appinfo->{salt});      my $salt = pack("H*", $appinfo->{salt});
   
Line 813 
Line 841 
         $CRYPTS[ $appinfo->{cipher} ]{DES_odd_parity},          $CRYPTS[ $appinfo->{cipher} ]{DES_odd_parity},
     );      );
   
       #print "Iter: '" . $appinfo->{iter} . "'\n";
     #print "Key:  '". unpack("H*", $key) . "'\n";      #print "Key:  '". unpack("H*", $key) . "'\n";
       #print "Salt: '". unpack("H*", $salt) . "'\n";
     #print "Hash: '". $hash . "'\n";      #print "Hash: '". $hash . "'\n";
     #print "Hash: '". $appinfo->{masterhash} . "'\n";      #print "Hash: '". $appinfo->{masterhash} . "'\n";
   
Line 876 
Line 906 
   
 sub _password_update_v4  sub _password_update_v4
 {  {
       require Digest::MD5;
       import Digest::MD5 qw(md5);
   
     my $pass = shift;      my $pass = shift;
   
     if (! defined $pass) { croak('No password specified!'); };      if (! defined $pass) { croak('No password specified!'); };
Line 924 
Line 957 
     return $key;      return $key;
 }  }
   
   # Helpers
   
 sub _calc_keys  sub _calc_keys
 {  {
Line 953 
Line 987 
 {  {
     my ($pass, $salt, $iter, $keylen, $dop) = @_;      my ($pass, $salt, $iter, $keylen, $dop) = @_;
   
       require Digest::HMAC_SHA1;
       import  Digest::HMAC_SHA1 qw(hmac_sha1);
       require Digest::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); }      if ($dop) { $key = DES_odd_parity($key); }
   
Line 963 
Line 1002 
   
 sub _crypt3des  sub _crypt3des
 {  {
       require Crypt::DES;
   
     my ( $plaintext, $passphrase, $flag ) = @_;      my ( $plaintext, $passphrase, $flag ) = @_;
   
     $passphrase   .= $SPACE x ( 16 * 3 );      $passphrase   .= $SPACE x ( 16 * 3 );
Line 1021 
Line 1062 
     $labels[3]   = 'lastchange';      $labels[3]   = 'lastchange';
     $labels[255] = 'notes';      $labels[255] = 'notes';
   
     my ($len) = unpack "S1", $field;      my ($len) = unpack "n1", $field;
     if ($len + 4 > length $field) {      if ($len + 4 > length $field) {
         return undef, $field;          return undef, $field;
     }      }
     my $unpackstr = "S1 C1 C1 A$len";      my $unpackstr = "x2 C1 C1 A$len";
     if ($len % 2 && $len + 4 < length $field) {      my $offset    =   2 +1 +1 +$len;
       if ($len % 2) { # && $len + 4 < length $field) {
         # trim the 0/1 byte padding for next even address.          # trim the 0/1 byte padding for next even address.
           $offset++;
         $unpackstr .= ' x'          $unpackstr .= ' x'
     }      }
     $unpackstr .= ' A*';  
   
     my (undef, $label, $font, $data, $leftover)      my ($label, $font, $data) = unpack $unpackstr, $field;
         = unpack $unpackstr, $field;      my $leftover = substr $field, $offset;
   
     if ($label == 3) {      if ($label == 3) {
         $data = _parse_keyring_date($data);          $data = _parse_keyring_date($data);
Line 1067 
Line 1109 
         $data = _pack_keyring_date($data);          $data = _pack_keyring_date($data);
     }      }
     my $len = length $data;      my $len = length $data;
     my $packstr = "S1 C1 C1 A*";      my $packstr = "n1 C1 C1 A*";
   
     my $packed = pack $packstr, ($len, $label, $font, $data);      my $packed = pack $packstr, ($len, $label, $font, $data);
   
Line 1302 
Line 1344 
   
 =back  =back
   
   =head2 crypt
   
   Pass in the alias of the crypt to use, or the index.
   
   This is a function, not a method.
   
       my $c = Palm::Keyring::crypt($cipher);
   
   $c is now:
   
       $c = {
           alias     => (None|DES_EDE3|AES128|AES256),
           name      => (None|DES_EDE3|Rijndael),
           keylen    => <key length of the ciphe>,
           blocksize => <block size of the cipher>,
           default_iter => <default iterations for the cipher>,
       };
   
   
 =head2 Encrypt  =head2 Encrypt
   
     $pdb->Encrypt($rec, $acct[, $password]);      $pdb->Encrypt($rec, $acct[, $password[, $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.
   
   $ivec is the initialization vector to use to encrypt the record.  This is
   not used by v4 databases.  Normally this is not passed and is generated
   randomly.
   
 $rec is a record from $pdb->{records} or a new_Record().  $rec is a record from $pdb->{records} or a new_Record().
 The v4 $acct is a hashref in the format below.  The v4 $acct is a hashref in the format below.

Legend:
Removed from v.1.31  
changed lines
  Added in v.1.35

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