version 1.10, 2006/12/06 18:45:42 |
version 1.13, 2007/01/28 18:13:28 |
|
|
# |
# |
# This started as Memo.pm, I just made it work for Keyring. |
# This started as Memo.pm, I just made it work for Keyring. |
# |
# |
# $Id$ |
# $RedRiver: Keyring.pm,v 1.12 2007/01/28 00:18:46 andrew Exp $ |
# $RedRiver: Keyring.pm,v 1.9 2006/11/10 17:49:51 andrew Exp $ |
|
|
|
use strict; |
use strict; |
package Palm::Keyring; |
package Palm::Keyring; |
|
|
|
|
=head1 SYNOPSIS |
=head1 SYNOPSIS |
|
|
use Palm::Keyring; |
use Palm::PDB; |
$pdb->Decrypt('mypassword'); |
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 |
|
|
=head1 DESCRIPTION |
=head1 DESCRIPTION |
|
|
The Keyring PDB handler is a helper class for the Palm::PDB package. It |
The Keyring PDB handler is a helper class for the Palm::PDB package. It |
parses Keyring databases. See |
parses Keyring for Palm OS databases. See |
L<http://gnukeyring.sourceforge.net/>. |
L<http://gnukeyring.sourceforge.net/>. |
|
|
It is just the standard Palm::Raw with 2 additional public methods. Decrypt and Encrypt. |
It has the standard Palm::PDB methods with 2 additional public methods. |
|
Decrypt and Encrypt. |
|
|
|
It currently supports the v4 Keyring databases. The v5 databases from the pre-release keyring-2.0 are not supported. |
|
|
=cut |
=cut |
|
|
=head2 new |
=head2 new |
|
|
$pdb = new Palm::Keyring ('password'); |
$pdb = new Palm::Keyring([$password]); |
|
|
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. |
Use this method if you're creating a Keyring PDB from scratch otherwise you |
|
can just use Palm::PDB::new(). |
|
|
=cut |
=cut |
#' |
|
sub new |
sub new |
{ |
{ |
my $classname = shift; |
my $classname = shift; |
|
|
# Give the PDB the first record that will hold the encrypted password |
# Give the PDB the first record that will hold the encrypted password |
$self->{records} = [ $self->new_Record ]; |
$self->{records} = [ $self->new_Record ]; |
|
|
if ($pass) { |
if (defined $pass) { |
$self->Encrypt($pass); |
$self->Encrypt($pass); |
} |
} |
|
|
|
|
); |
); |
} |
} |
|
|
|
=pod |
|
|
|
=head2 Load |
|
|
|
$pdb->Load($filename[, $password]); |
|
|
|
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. |
|
|
|
It also takes an additional optional parameter, which is the password to use to |
|
decrypt the database. |
|
|
|
See Decrypt() for the additional fields that are available after decryption. |
|
|
|
=cut |
|
|
sub Load |
sub Load |
{ |
{ |
my $self = shift; |
my $self = shift; |
|
my $filename = shift; |
|
my $password = shift; |
|
|
$self->{'appinfo'} = {}; |
$self->{'appinfo'} = {}; |
$self->{'records'} = []; |
$self->{'records'} = []; |
$self->SUPER::Load(@_); |
$self->SUPER::Load($filename); |
|
|
foreach my $record (@{ $self->{records} }) { |
foreach my $record (@{ $self->{records} }) { |
next unless exists $record->{data}; |
next unless exists $record->{data}; |
|
|
$record->{plaintext}->{name} = $name; |
$record->{plaintext}->{name} = $name; |
$record->{encrypted} = $encrypted; |
$record->{encrypted} = $encrypted; |
} |
} |
|
|
|
return $self->Decrypt($password) if defined $password; |
|
|
1; |
1; |
} |
} |
|
|
|
=pod |
|
|
|
=head2 Write |
|
|
|
$pdb->Write($filename[, $password]); |
|
|
|
Just like the Palm::Raw::Write() but encrypts everything before saving. |
|
|
|
Also takes an optional password to encrypt with a new password, not needed |
|
unless you are changing the password. |
|
|
|
=cut |
|
|
sub Write |
sub Write |
{ |
{ |
my $self = shift; |
my $self = shift; |
$self->Encrypt() || return undef; |
my $filename = shift; |
return $self->SUPER::Write(@_); |
my $password = shift; |
|
|
|
$self->Encrypt($password) || return undef; |
|
return $self->SUPER::Write($filename); |
} |
} |
|
|
|
=pod |
|
|
|
=head2 Encrypt |
|
|
|
$pdb->Encrypt([$password]); |
|
|
|
Encrypts the PDB, either with the password used to decrypt or create it, or |
|
optionally with a password that is passed. |
|
|
|
See Decrypt() for an what plaintext fields are available to be encrypted. |
|
|
|
=cut |
|
|
sub Encrypt |
sub Encrypt |
{ |
{ |
my $self = shift; |
my $self = shift; |
my $pass = shift; |
my $pass = shift; |
|
|
|
|
if ($pass) { |
if ($pass) { |
unless ($self->_keyring_verify($pass) ) { |
unless (exists $self->{'records'}->[0]->{'data'} && |
|
$self->_keyring_verify($pass) ) { |
# This would encrypt with a new password. |
# This would encrypt with a new password. |
# First decrypting everything with the old password of course. |
# First decrypting everything with the old password of course. |
$self->_keyring_update($pass) || return undef; |
$self->_keyring_update($pass) || return undef; |
|
|
$record->{data} = join("\000", $name, $encrypted); |
$record->{data} = join("\000", $name, $encrypted); |
} |
} |
|
|
return 1; |
1; |
} |
} |
|
|
|
=head2 Decrypt |
|
|
|
$pdb->Decrypt([$password]); |
|
|
|
Decrypts the PDB and fills out the rest of the fields available in |
|
$record->{'plaintext'}. |
|
|
|
The plaintext should now be this, before encryption or after decryption: |
|
|
|
$record->{'plaintext'} = { |
|
name => $name, |
|
account => $account, |
|
password => $account_password, |
|
description => $description, |
|
}; |
|
|
|
=cut |
|
|
sub Decrypt |
sub Decrypt |
{ |
{ |
my $self = shift; |
my $self = shift; |
|
|
|
|
$self->{digest} ||= _calc_keys($self->{password}); |
$self->{digest} ||= _calc_keys($self->{password}); |
|
|
|
my $recordcount = 0; |
foreach my $record (@{ $self->{records} }) { |
foreach my $record (@{ $self->{records} }) { |
next unless defined $record->{data}; |
$recordcount++; |
|
# always skip the first record that has the password in it. |
|
next if $recordcount <= 1; |
|
unless (defined $record->{data}) { |
|
warn "Invalid record " . ($recordcount - 1) . "\n"; |
|
next; |
|
} |
|
|
my ($name, $encrypted) = split /\000/, $record->{data}, 2; |
my ($name, $encrypted) = split /\000/, $record->{data}, 2; |
next unless $encrypted; |
next unless $encrypted; |
|
|
|
|
} |
} |
|
|
return 1; |
1; |
} |
} |
|
|
sub _calc_keys |
sub _calc_keys |
|
|
my $pass = shift; |
my $pass = shift; |
|
|
die "No password specified!" unless $pass; |
die "No password specified!" unless $pass; |
$self->{password} = $pass; |
|
|
|
# AFAIK the thing we use to test the password is |
# AFAIK the thing we use to test the password is |
# always in the first entry |
# always in the first entry |
|
|
|
|
if ($data eq $salt . $digest) { |
if ($data eq $salt . $digest) { |
# May as well generate the keys we need now, since we know the password is right |
# May as well generate the keys we need now, since we know the password is right |
$self->{digest} = _calc_keys($self->{password}); |
$self->{digest} = _calc_keys($pass); |
if ($self->{digest}) { |
if ($self->{digest}) { |
|
$self->{password} = $pass; |
return 1; |
return 1; |
} else { |
} else { |
return undef; |
return undef; |
|
|
return 1; |
return 1; |
} |
} |
|
|
|
|
# XXX It looks like they are using des_ecb2_encrypt so I dunno if that is different |
|
sub _crypt3des { |
sub _crypt3des { |
my ( $plaintext, $passphrase, $flag ) = @_; |
my ( $plaintext, $passphrase, $flag ) = @_; |
my $NULL = chr(0); |
my $NULL = chr(0); |
|
|
$C[$_] = new Crypt::DES( pack( "H*", substr($passphrase, 16*$_, 16 ))); |
$C[$_] = new Crypt::DES( pack( "H*", substr($passphrase, 16*$_, 16 ))); |
} |
} |
|
|
|
|
# XXX From Crypt::TripleDES |
|
# http://search.cpan.org/src/VIPUL/Crypt-TripleDES-0.24/lib/Crypt/TripleDES.pm |
|
# |
|
# for ( 0 .. (($size)/8) -1 ) { |
|
# my $pt = substr( $plaintext, $_*8, 8 ); |
|
# $pt = Crypt::PPDES::des_ecb_encrypt( $flag ? $keyvecs{0} : $keyvecs{2}, $flag, $pt ); |
|
# $pt = Crypt::PPDES::des_ecb_encrypt( $keyvecs{1}, (not $flag), $pt ); |
|
# $pt = Crypt::PPDES::des_ecb_encrypt( $flag ? $keyvecs{2} : $keyvecs{0}, $flag, $pt ); |
|
# $cyphertext .= $pt; |
|
# } |
|
|
|
for ( 0 .. (($size)/8)) { |
for ( 0 .. (($size)/8)) { |
my $pt = substr( $plaintext, $_*8, 8 ); |
my $pt = substr( $plaintext, $_*8, 8 ); |
#print "PT: '$pt' - Length: " . length($pt) . "\n"; |
#print "PT: '$pt' - Length: " . length($pt) . "\n"; |
|
|
|
|
=head1 AUTHOR |
=head1 AUTHOR |
|
|
Andrew Fresh E<lt>andrew@mad-techies.org<gt> |
Andrew Fresh E<lt>andrew@mad-techies.orgE<gt> |
|
|
=head1 SEE ALSO |
=head1 SEE ALSO |
|
|
Palm::PDB(3) |
Palm::PDB(3) |
|
|
Palm::StdAppInfo(3) |
Palm::StdAppInfo(3) |
|
|
|
The Keyring for Palm OS website: |
|
L<http://gnukeyring.sourceforge.net/> |
|
|
=cut |
=cut |