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

Annotation of palm/Palm-Keyring/lib/Palm/Keyring.pm, Revision 1.1

1.1     ! andrew      1: # Palm::Keyring.pm
        !             2: #
        !             3: # Perl class for dealing with Keyring for Palm OS databases.
        !             4: #
        !             5: #      Copyright (C) 2004, Andrew Fresh
        !             6: #      You may distribute this file under the terms of the Artistic
        !             7: #      License, as specified in the README file distributed with the p5-Palm distribution.
        !             8: #
        !             9: #   This started as Memo.pm, I just made it work for Keyring.
        !            10: #
        !            11: # $Id$
        !            12: # $RedRiver$
        !            13:
        !            14: use strict;
        !            15: package Palm::Keyring;
        !            16: use Palm::Raw();
        !            17: use Palm::StdAppInfo();
        !            18: use vars qw( $VERSION @ISA );
        !            19:
        !            20: use Digest::MD5 qw(md5);
        !            21: use Crypt::TripleDES;
        !            22:
        !            23: use constant ENCRYPT    =>  1;
        !            24: use constant DECRYPT    =>  0;
        !            25: use constant MD5_CBLOCK => 64;
        !            26: my $kSaltSize = 4;
        !            27:
        !            28:
        !            29: # One liner, to allow MakeMaker to work.
        !            30: $VERSION = do { my @r = (q$Revision: 0.01 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
        !            31:
        !            32: @ISA = qw( Palm::StdAppInfo Palm::Raw );
        !            33:
        !            34: =head1 NAME
        !            35:
        !            36: Palm::Keyring - Handler for Palm Keyring databases.
        !            37:
        !            38: =head1 SYNOPSIS
        !            39:
        !            40:     use Palm::Keyring;
        !            41:        $pdb->Decrypt('mypassword');
        !            42:
        !            43: =head1 DESCRIPTION
        !            44:
        !            45: The Keyring PDB handler is a helper class for the Palm::PDB package. It
        !            46: parses Keyring databases.  See
        !            47: L<http://gnukeyring.sourceforge.net/>.
        !            48:
        !            49: It is just the standard Palm::Raw with 2 additional public methods.  Decrypt and Encrypt.
        !            50:
        !            51: =cut
        !            52: =head2 new
        !            53:
        !            54:   $pdb = new Palm::Keyring ('password');
        !            55:
        !            56: Create a new PDB, initialized with the various Palm::Keyring fields
        !            57: and an empty record list.
        !            58:
        !            59: Use this method if you're creating a Keyring PDB from scratch.
        !            60:
        !            61: =cut
        !            62: #'
        !            63: sub new
        !            64: {
        !            65:        my $classname   = shift;
        !            66:        my $pass = shift;
        !            67:
        !            68:        my $self        = $classname->SUPER::new(@_);
        !            69:                        # Create a generic PDB. No need to rebless it,
        !            70:                        # though.
        !            71:
        !            72:        $self->{name} = "Keys-Gtkr";    # Default
        !            73:        $self->{creator} = "Gtkr";
        !            74:        $self->{type} = "Gkyr";
        !            75:        $self->{attributes}{resource} = 0;
        !            76:                                # The PDB is not a resource database by
        !            77:                                # default, but it's worth emphasizing,
        !            78:                                # since MemoDB is explicitly not a PRC.
        !            79:
        !            80:        # Initialize the AppInfo block
        !            81:        $self->{appinfo} = {};
        !            82:
        !            83:        # Add the standard AppInfo block stuff
        !            84:        &Palm::StdAppInfo::seed_StdAppInfo($self->{appinfo});
        !            85:
        !            86:        # Set the version
        !            87:        $self->{version} = 4;
        !            88:
        !            89:        # Give the PDB the first record that will hold the encrypted password
        !            90:        $self->{records} = [
        !            91:                {
        !            92:                  'category' => 0,
        !            93:                  'attributes' => {
        !            94:                                                        'private' => 1,
        !            95:                                                        'Secret' => 1,
        !            96:                                                        'Dirty' => 1,
        !            97:                                                        'dirty' => 1
        !            98:                                                  },
        !            99:                },
        !           100:        ];
        !           101:
        !           102:        if ($pass) {
        !           103:                $self->Encrypt($pass);
        !           104:        }
        !           105:
        !           106:        return $self;
        !           107: }
        !           108:
        !           109: sub import
        !           110: {
        !           111:        &Palm::PDB::RegisterPDBHandlers(__PACKAGE__,
        !           112:                [ "Gtkr", "Gkyr" ],
        !           113:                );
        !           114: }
        !           115:
        !           116: sub Encrypt
        !           117: {
        !           118:        my $self = shift;
        !           119:        my $pass = shift;
        !           120:
        !           121:        if ($pass) {
        !           122:                unless ($self->_keyring_verify($pass) ) {
        !           123:                        # This would encrypt with a new password.
        !           124:                        # First decrypting everything with the old password of course.
        !           125:                        $self->_keyring_update($pass) || return undef;
        !           126:                        $self->_keyring_verify($pass) || return undef;
        !           127:                }
        !           128:        }
        !           129:
        !           130:        my $seen_enc_pass = 0;
        !           131:        foreach my $record (@{ $self->{records} }) {
        !           132:                unless ($seen_enc_pass) {
        !           133:                        $seen_enc_pass = 1;
        !           134:                        next;
        !           135:                }
        !           136:
        !           137:                next unless defined $record->{plaintext};
        !           138:
        !           139:                my $name        = defined $record->{plaintext}->{name}        ? $record->{plaintext}->{name}        : '';
        !           140:                my $account     = defined $record->{plaintext}->{account}     ? $record->{plaintext}->{account}     : '';
        !           141:                my $password    = defined $record->{plaintext}->{password}    ? $record->{plaintext}->{password}    : '';
        !           142:                my $description = defined $record->{plaintext}->{description} ? $record->{plaintext}->{description} : '';
        !           143:                my $extra       = '';
        !           144:
        !           145:                my $plaintext = join("\0", $account, $password, $description, $extra);
        !           146:
        !           147:                my $encrypted = $self->_crypt($plaintext, ENCRYPT);
        !           148:
        !           149:                $record->{data} = join("\0", $name, $encrypted);
        !           150:        }
        !           151:
        !           152:        return 1;
        !           153: }
        !           154:
        !           155: sub Decrypt
        !           156: {
        !           157:        my $self = shift;
        !           158:        my $pass = shift;
        !           159:
        !           160:        if ($pass) {
        !           161:                $self->_keyring_verify($pass) || return undef;
        !           162:        }
        !           163:
        !           164:        my $seen_enc_pass = 0;
        !           165:        foreach my $record (@{ $self->{records} }) {
        !           166:                unless ($seen_enc_pass) {
        !           167:                        # need to skip the first record because it is the encrypted password
        !           168:                        $seen_enc_pass = 1;
        !           169:                        next;
        !           170:                }
        !           171:
        !           172:                next unless defined $record->{data};
        !           173:
        !           174:                my ($name, $encrypted) = split /\0/, $record->{data};
        !           175:                $record->{plaintext}->{name} = $name;
        !           176:
        !           177:                my $decrypted = $self->_crypt($encrypted, DECRYPT);
        !           178:                my ($account, $password, $description, $extra)
        !           179:                      = split /\0/, $decrypted, 4;
        !           180:
        !           181:                $record->{plaintext}->{account}     = defined $account     ? $account     : '';
        !           182:                $record->{plaintext}->{password}    = defined $password    ? $password    : '';
        !           183:                $record->{plaintext}->{description} = defined $description ? $description : '';
        !           184:
        !           185:                #print "Extra: $extra\n";
        !           186:                #--------------------------------------------------
        !           187:                # print "Account:     $account\n";
        !           188:                # print "Password:    $password\n";
        !           189:                # print "Description: $description\n";
        !           190:                #--------------------------------------------------
        !           191:
        !           192:        }
        !           193:
        !           194:        return 1;
        !           195: }
        !           196:
        !           197: sub _crypt
        !           198: {
        !           199:        my $self = shift;
        !           200:        my $original = shift;
        !           201:        my $flag = shift;
        !           202:
        !           203:        my $digest = $self->{digest} || $self->_calc_keys();
        !           204:        #print "DIGEST: $digest\n";
        !           205:
        !           206:        my $des = new Crypt::TripleDES;
        !           207:
        !           208:        if ($flag == ENCRYPT) {
        !           209:                return $des->encrypt3($original, $digest);
        !           210:        } else {
        !           211:                return $des->decrypt3($original, $digest);
        !           212:        }
        !           213: }
        !           214:
        !           215: sub _calc_keys
        !           216: {
        !           217:        my $self = shift;
        !           218:
        !           219:        my $pass = $self->{'password'};
        !           220:        die "No password defined!" unless defined $pass;
        !           221:
        !           222:        my $digest = md5($pass);
        !           223:
        !           224:        my ($key1, $key2) = unpack('a8a8', $digest);
        !           225:        #--------------------------------------------------
        !           226:        # print "key1: $key1: ", length $key1, "\n";
        !           227:        # print "key2: $key2: ", length $key2, "\n";
        !           228:        #--------------------------------------------------
        !           229:
        !           230:        $digest = unpack('H*', $key1 . $key2 . $key1);
        !           231:        #--------------------------------------------------
        !           232:        # print "Digest: ", $digest, "\n";
        !           233:        # print length $digest, "\n";
        !           234:        #--------------------------------------------------
        !           235:
        !           236:        $self->{digest} = $digest;
        !           237:        return $digest;
        !           238: }
        !           239:
        !           240: sub _keyring_verify
        !           241: {
        !           242:        my $self = shift;
        !           243:        my $pass = shift;
        !           244:
        !           245:        die "No password specified!" unless defined $pass;
        !           246:        $self->{password} = $pass;
        !           247:
        !           248:        # AFAIK the thing we use to test the password is
        !           249:        #     always in the first entry
        !           250:        my $data = $self->{records}->[0]->{data};
        !           251:        #die "No encrypted password in file!" unless defined $data;
        !           252:        return undef unless defined $data;
        !           253:
        !           254:        $data =~ s/\0$//;
        !           255:
        !           256:        my $salt = substr($data, 0, $kSaltSize);
        !           257:
        !           258:        my $msg = $salt . $pass;
        !           259:
        !           260:        $msg .= "\0" x (MD5_CBLOCK - length($msg));
        !           261:
        !           262:        my $digest = md5($msg);
        !           263:
        !           264:        if ($data eq $salt . $digest) {
        !           265:                # May as well generate the keys we need now, since we know the password is right
        !           266:                if ($self->_calc_keys()) {
        !           267:                        return 1;
        !           268:                } else {
        !           269:                        return undef;
        !           270:                }
        !           271:        } else {
        !           272:                return undef;
        !           273:        }
        !           274: }
        !           275:
        !           276: sub _keyring_update
        !           277: {
        !           278:        # It is very important to Encrypt after calling this
        !           279:        #     (Although it is generally only called by Encrypt)
        !           280:        # because otherwise the data will be out of sync with the
        !           281:        # password, and that would suck!
        !           282:        my $self = shift;
        !           283:        my $pass = shift;
        !           284:
        !           285:        die "No password specified!" unless defined $pass;
        !           286:
        !           287:        # if the database already has a password in it
        !           288:        if ($self->{records}->[0]->{data}) {
        !           289:                # Make sure everything is decrypted before we update the keyring
        !           290:                $self->Decrypt() || return undef;
        !           291:        }
        !           292:
        !           293:        my $salt;
        !           294:        for (1..$kSaltSize) {
        !           295:                $salt .= chr(int(rand(255)));
        !           296:        }
        !           297:
        !           298:        my $msg = $salt . $pass;
        !           299:
        !           300:        $msg .= "\0" x (MD5_CBLOCK - length($msg));
        !           301:
        !           302:        my $digest = md5($msg);
        !           303:
        !           304:        my $data = $salt . $digest;# . "\0";
        !           305:
        !           306:        # AFAIK the thing we use to test the password is
        !           307:        #     always in the first entry
        !           308:        $self->{records}->[0]->{data} = $data;
        !           309:
        !           310:        $self->{password} = $pass;
        !           311:        $self->_calc_keys();
        !           312:
        !           313:        return 1;
        !           314: }
        !           315:
        !           316:
        !           317: 1;
        !           318: __END__
        !           319:
        !           320: =head1 AUTHOR
        !           321:
        !           322: Andrew Fresh E<lt>andrew@mad-techies.org<gt>
        !           323:
        !           324: =head1 SEE ALSO
        !           325:
        !           326: Palm::PDB(3)
        !           327:
        !           328: Palm::StdAppInfo(3)
        !           329:
        !           330: =cut

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