[ here is the POD for a module I wrote this weekend - I'd appreciate
feedback. It's available to play with via :
http://www.people.virginia.edu/~ajm6q/Data-Encrypted-0.01.tar.gz
I'm sure there are still lots of bugs, but I wanted to "get it out there"
for people to comment on before I spent a lot of maintenance time
-- Aaron ]
NAME
Data::Encrypted - Transparently store encrypted data via RSA
SYNOPSIS
use Data::Encrypted file => './secret', qw(encrypted);
# note: 'login' and 'password' are not *really* the login and
# password values!
my $login = encrypted('login');
my $password = encrypted('password');
[ then, at the command line: ]
% perl myscript.pl
Data::Encrypted value for 'login' not found, please enter: *****
Data::Encrypted value for 'password' not found, please enter: ********
[ script merrily continues ... ]
% perl myscript.pl
[ script merrily continues, no prompting ... ]
DESCRIPTION
Often when dealing with other systems (database engines, ftp, telnet,
etc), your Perl script must supply a password to the other system. This
requires you to store the password in plaintext in your script. If you
share your script with anyone (as any good open-source developer would),
you'd rather not have your password or other sensitive information
floating around.
Data::Encrypted works by prompting you (via Term::ReadPassword) for the
real value(s) required by each call to 'encrypted', but only the first
time you run the script; thereafter, the data is stored encrypted in a
secondary file. Subsequent executions of your script use the encrypted
data directly, if possible, otherwise prompting again for the data.
Encrypted Data Storage
You may provide Data::Encrypted with a filename or already opened
filehandle for encrypted data storage; alternatively you may use
"virtual files" at the end of your script for encrypted data storage via
Inline::Files:
use Inline::Files;
use Data::Encrypted;
open(ENCRYPT, '+<');
my $encyptor = new Data::Encrypted FH => \*ENCRYPT;
my $password = $encryptor->encrypt('password');
Then, Data::Encrypted will read/write its data from the special
__ENCRYPT__ virtual file (see the Inline::Files manpage for more
information and a better description of virtual files). This way
everything stays contained within your script; no accessory storage file
is necessary. If you send the script to someone else, and they try to
run it, the RSA authentication will fail, but they will simply be
prompted for the values as you were when you first ran the script. When
they enter the values the __ENCRYPT__ virtual file will be rewritten,
and they may continue to use the script. In this way Data::Encrypted is
really a "personalization" utility, ensuring that the encrypted data can
only be unlocked by one person.
Resetting Encrypted Values
If, after the initial execution and value specification, you need to
reset or clear the stored values once so that you may specify new ones,
you can invoke your script like so:
perl -MData::Encrypted=reset myscript.pl
Alternatively, simply add the 'reset' imperative to your use statement:
use Data::Encrypted file => './secret', qw(encrypted reset);
This would force the user to enter the required data on every invocation
(which might be useful for yourself while you tried to rememeber just
what that lost database password was ...); even though the encrypted
data is available, it will be stored afresh on each invocation.
RSA Authentication
Data::Encrypted uses RSA authentication to encrypt and decrypt its data.
It achieves this by reading the user's public and private RSA keys. By
default, Data::Encrypted assumes these files are stored in the .ssh
subdirectory of their home directory (found using File::HomeDir), but
you can provide alternative key files yourself, either by supplying
alternative key filenames, or by building Crypt::RSA::Key's yourself:
use Data::Encrypted FILE => './secret';
use Crypt::RSA::Key;
my $public = new Crypt::RSA::Key::Public::SSH
Filename => '~/.ssh/identity.pub';
# my private key includes a passphrase!
my $private = new Crypt::RSA::Key::Private::SSH
Filename => '~/.ssh/identity',
Password => q(These are the times that try men's souls);
my $d = new Data::Encrypted (PK => $public, SK => $private);
my $password = $d->encrypted('password');
Or, more directly:
use Data::Encrypted (FILE => './secret',
PK => '~/.ssh/identity.pub',
SK => '~/.ssh/identity',
PW => q(These are the times that try men's souls)
), qw(encrypted);
my $password = encrypted('password');
Real Life Examples
example #1 - a DBI session, utilizing virtual file storage:
use DBI;
use Inline::Files;
use Data::Encrypted;
open(ENCRYPT, '+<') or die $!;
my $encryptor = new Data::Encrypted FH => \*ENCRYPT';
my $dbh = DBI->connect('dbi:mysql:mydatabase',
$encryptor->encrypt('user name'),
$encryptor->encrypt('db password'),
{ RaiseError => 1, AutoCommit => 1}
);
[.. continue using $dbh ...]
example #2 - an FTP session, with 'reset' (will *always* prompt for data
until 'reset' is removed from use statement):
use Net::FTP;
use Data::Encrypted FILE => './.ftplogin', qw(encrypted reset);
my $ftp = new Net::FTP 'ftp.somewhere.com';
$ftp->login(encrypted('ftp user'),
encrypted('ftp password'));
$ftp->cwd('/pub');
[... continue using $ftp ...]
example #3 - a POP3 email client session with "non-standard" RSA keys:
use Mail::POP3Client;
use Crypt::RSA::Key;
use Data::Encrypted;
my $public = new Crypt::RSA::Key::Public::SSH
Filename => '~/.ssh2/id_dsa_1024_a.pub';
my $public = new Crypt::RSA::Key::Private::SSH
Filename => '~/.ssh2/id_dsa_1024_a'
Password => 'The Only Good Computer Is A Dead Computer';
my $encryptor = new Data::Encrypted ( FILE => './.pop3email',
SK => $private,
PK => $public
);
my $pop3 = new Mail::POP3Client
( USER => $encryptor->encrypt('user'),
PASSWORD => $encryptor->encrypt('password'),
HOST => $encryptor->encrypt('host')
);
example #4 - build a script to send to Bob that allows him to ftp files
from you, without passing along your connection information in clear
text; note that you yourself won't be able to actually use the script
without entering the data each time. Also note that Bob could easily
read the encrypted information, so it is not secure from him.
use Data::Encrypted;
use Net::FTP;
use Inline::Files;
open(ENCRYPT, "+<") or die $!;
my $encryptor = new Data::Encrypted FH => \*ENCRYPT,
PK => '~/pubkeys/bob.pub';
my $ftp = new Net::FTP "ftp.mysite.org";
$ftp->login($encryptor->encrypt('user'),
$encryptor->encrypt('password'));
$ftp->cwd($encryptor->encrypt(q{Bob's directory}));
$ftp->get($encryptor->encrypt(q{What Bob get's to download}));
$ftp->quit();
BUGS/TODO
Inline::Files won't (yet) allow one package (i.e. Data::Encrypted) to
work with virtual files in another package (i.e. main); as a result, you
have to feed your storage file to Data::Encrypted manually. Not so much
a bug as a drawback.
Files are not (currently) locked, so all bets are off in a multi-tasking
environment. Again, not a bug, but a limitation to be aware of.
This idea could be extended to the Tie::EncryptedHash or other
Crypt::CBC-employing methodology, but would lose the fundamental
advantage of not requiring any clear text password or passphrase to
remain associated with the script. Someone may convince me otherwise.
When Data::Encrypted prompts for unknown data, it should really ask the
user to repeat their data entry for validation, to cut down on the
possibility of typos.
Data::Encrypted should be able to automatically look up keys in .ssh2/
directories as well as the .ssh/ identity files ... for some other day!
COPYRIGHT
Copyright (c) 2001 Aaron J Mackey. All rights reserved.
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
AUTHOR
Aaron J Mackey, [EMAIL PROTECTED]
ORIGINAL IDEA
William R. Pearson, [EMAIL PROTECTED]
SEE ALSO
Crypt::RSA, Inline::Files, Term::ReadPassword, File::HomeDir, perl(1).