Crypt-GPG

 view release on metacpan or  search on metacpan

GPG.pm  view on Meta::CPAN

# -*-cperl-*-
#
# Crypt::GPG - An Object Oriented Interface to GnuPG.
# Copyright (c) 2000-2007 Ashish Gulhati <crypt-gpg at neomailbox.com>
#
# All rights reserved. This code is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: GPG.pm,v 1.64 2014/09/18 12:21:25 ashish Exp $

package Crypt::GPG;

use Carp;

GPG.pm  view on Meta::CPAN

use File::Spec ();
use Date::Parse;
use File::Temp qw( tempfile tempdir );
use IPC::Run qw( start pump finish timeout );
use vars qw( $VERSION $AUTOLOAD );

File::Temp->safe_level( File::Temp::STANDARD );
( $VERSION ) = '$Revision: 1.64 $' =~ /\s+([\d\.]+)/;

sub new {
  bless { GPGBIN         =>   '/usr/local/bin/gpg',
	  FORCEDOPTS     =>   '--no-secmem-warning',
	  GPGOPTS        =>   '--lock-multiple --compress-algo 1 ' .
	                      '--cipher-algo cast5 --force-v3-sigs',
	  VERSION        =>   $VERSION,
	  DELAY          =>   0,
	  PASSPHRASE     =>   '',
	  COMMENT        =>   "Crypt::GPG v$VERSION",
	  ARMOR          =>   1,
	  MARGINALS      =>   3,
	  DETACH         =>   1,

GPG.pm  view on Meta::CPAN

#  $message .= "\n" unless $message =~ /\n$/s;
  $message =~ s/(?<!\r)\n/\r\n/sg;
  print $tmpfh $message; close $tmpfh; 

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  push (@opts, ('--comment', $self->comment)) if $self->comment;
  my $signhow = $self->clearsign ? '--clearsign' : '--sign';
  local $SIG{CHLD} = 'IGNORE';

  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, @secretkey,'--no-tty', '--status-fd', '2', '--command-fd', 
		  0, '-o-', $signhow, @extras, $tmpnam], \$in, \$out, \$err, timeout( 30 ));
  my $skip = 1; my $i = 0;
  local $SIG{CHLD} = 'IGNORE';
  local $SIG{PIPE} = 'IGNORE';
  while ($skip) {
    pump $h until ($err =~ /NEED_PASSPHRASE (.{16}) (.{16}).*\n/g or
		   $err =~ /GOOD_PASSPHRASE/g);
    if ($2) {
      $in .= $self->passphrase . "\n";
      pump $h until $err =~ /(GOOD|BAD)_PASSPHRASE/g;

GPG.pm  view on Meta::CPAN

  my ($tf, $ts, $td) = ($self->tmpfiles, $self->tmpsuffix, $self->tmpdir);
  my ($tmpfh, $tmpnam) = tempfile ($tf, DIR => $td, SUFFIX => $ts, UNLINK => 1);
  my ($tmpfh2, $tmpnam2) = tempfile ($tf, DIR => $td, SUFFIX => $ts, UNLINK => 1);

  my $ciphertext = ref($_[0]) ? join '', @{$_[0]} : $_[0];
  $ciphertext .= "\n" unless $ciphertext =~ /\n$/s;
  print $tmpfh $ciphertext; close $tmpfh;

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  push (@opts, ('--comment', $self->comment)) if $self->comment and !$_[1];
  backtick ($self->gpgbin, @opts, '--marginals-needed', $self->marginals, '--check-trustdb');

    local $SIG{CHLD} = 'IGNORE';
    local $SIG{PIPE} = 'IGNORE';

  my $x;
  if ($_[1]) {
    my $message = ref($_[1]) ? join '', @{$_[1]} : $_[1];
#    $message .= "\n" unless $message =~ /\n$/s;
    $message =~ s/(?<!\r)\n/\r\n/sg;
    ($tmpfh3, $tmpnam3) = tempfile ($tf, DIR => $td, SUFFIX => $ts, UNLINK => 1);
    print $tmpfh3 $message; close $tmpfh3;
    my $y = $self->gpgbin . " @opts --marginals-needed " . $self->marginals . " --status-fd 1 --logger-fd 1 --command-fd 0 --no-tty --verify $tmpnam $tmpnam3";
    $x = `$y`;
  }

  else {
    my ($in, $out, $err, $in_q, $out_q, $err_q);
    my $h = start ([$self->gpgbin, @opts, '--marginals-needed', $self->marginals,
                    '--status-fd', '1', '--command-fd', 0, '--yes', '--no-tty', 
		    '--decrypt', '-o', $tmpnam2, $tmpnam], 
		   \$in, \$out, \$err, timeout( 30 ));

    my $success = 0;
    my $seckey = (ref($self->secretkey)?$self->secretkey->{ID}:$self->secretkey);

    while (1) {
      pump $h until ($out =~ /NEED_PASSPHRASE (.{16}) (.{16}).*\n/g
		     or $out =~ /(GOOD_PASSPHRASE)/g

GPG.pm  view on Meta::CPAN

  my $self = shift; 
  my @return;
  
  my ($tmpfh, $tmpnam) = 
    tempfile( $self->tmpfiles, DIR => $self->tmpdir, 
 	      SUFFIX => $self->tmpsuffix, UNLINK => 1);
  warn join '',@{$_[0]};
  print $tmpfh join '',@{$_[0]}; close $tmpfh;
  
  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  my ($info) = backtick ($self->gpgbin, @opts, '--status-fd', 1, '--no-tty', '--batch', $tmpnam); 
  $info =~ s/ENC_TO (.{16})/{push @return, $1}/sge; 
  unlink $tmpnam;
  return @return;
}

sub encrypt {
  my $self = shift; 
  my ($message, $rcpts) = @_;
  my $info;

GPG.pm  view on Meta::CPAN


  $message = join ('', @$message) if ref($message) eq 'ARRAY'; 
  print $tmpfh $message; close $tmpfh;

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  push (@opts, '--default-key', ref($self->secretkey)?$self->secretkey->{ID}:$self->secretkey) if $sign and $self->secretkey;
  push (@opts, $sign) if $sign; push (@opts, $armor) if $armor;
  push (@opts, ('--comment', $self->comment)) if $self->comment;

  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, '--no-tty', '--status-fd', '1', '--command-fd', 0,
                  '-o', $tmpnam2, @rcpts, '--encrypt', $tmpnam], \$in, \$out, \$err, timeout( 30 ));
  local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';
  my $pos;
  eval {
    pump $h until ($out =~ /(o)penfile.overwrite.okay/g
		   or $out =~ /(u)(n)trusted_key.override/g    #! Test
		   or $out =~ /(k)(e)(y) not found/g           #! Test
		   or $out =~ /(p)(a)(s)(s)phrase.enter/g);
    $pos = 1 if $1; $pos = 2 if $2; $pos = 3 if $3; $pos = 4 if $4;
  };

GPG.pm  view on Meta::CPAN

  return if grep { $_ !~ /^[a-f0-9]+$/i } @keyids;

  my $tmpdir = tempdir( $self->tmpdirs, 
		     DIR => $self->tmpdir, CLEANUP => 1);
  my ($tmpfh, $tmpnam) = 
    tempfile( $self->tmpfiles, DIR => $self->tmpdir, 
	      SUFFIX => $self->tmpsuffix, UNLINK => 1);
  print $tmpfh $key;

  my @pret1 = ('--options', '/dev/null', '--homedir', $tmpdir);
  my @pret2 = ('--keyring', "$tmpdir/pubring.gpg", 
	       '--secret-keyring', "$tmpdir/secring.gpg");
  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  my @listopts = qw(--fingerprint --fingerprint --with-colons);

  backtick($self->gpgbin, @opts, @pret1, '-v', '--import', $tmpnam);
  backtick ($self->gpgbin, @opts, '--marginals-needed', $self->marginals, '--check-trustdb');
  my ($keylist) = backtick($self->gpgbin, @opts, @pret1, '--marginals-needed',
			   $self->marginals, '--check-sigs', @listopts, @keyids); 
  my ($seclist) = backtick($self->gpgbin, @opts, @pret1,
			   '--list-secret-keys', @listopts);

  my @seckeys = grep { my $id = $_->{ID}; 
		       (grep { $id eq $_ } @keyids) ? $_ : '' } 
    $self->parsekeys(split /\n/,$seclist);
  my @ret = ($self->parsekeys(split /\n/,$keylist), @seckeys);

  if ($pretend) {
#! This hack needed to get real calc trusts for to-import keys. Test!
    backtick ($self->gpgbin, @opts, '--marginals-needed', $self->marginals, '--check-trustdb');
    ($keylist) = backtick($self->gpgbin, @opts, @pret2, '--marginals-needed',
			  $self->marginals, '--check-sigs', @listopts); 

    my @realkeylist = grep { my $id = $_->{ID} if $_; 
			     $id and grep { $id eq $_->{ID} } @ret } 
#      map { ($_->{Keyring} eq "$tmpdir/secring.gpg" 
#	     or $_->{Keyring} eq "$tmpdir/pubring.gpg") ? $_ : 0 } 
	$self->parsekeys(split /\n/,$keylist);
    @ret = (@realkeylist, @seckeys);
  }
  else {
    if (@keyids) {
      my ($out) = backtick($self->gpgbin, @opts, @pret1, "--export", '-a', @keyids);
      print $tmpfh $out; close $tmpfh;
    }
    backtick($self->gpgbin, @opts, '-v', '--import', $tmpnam);
  }
  rmtree($tmpdir, 0, 1);
  unlink($tmpnam);
  return @ret;
}

sub export {
  my $self = shift; 
  my $key = shift; 
  my $id = $key->{ID};
  return unless $id =~ /$self->{VKEYID}/;

  my $armor = $self->armor ? '-a' : '';
  my $secret = $key->{Type} eq 'sec' ? '-secret-keys' : '';
  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  push (@opts, ('--comment', $self->comment)) if $self->comment;
  push (@opts, '--status-fd', '1');

  my ($out) = backtick($self->gpgbin, @opts, "--export$secret", $armor, $id);
  $out;
}

sub keygen {
  my $self = shift; 
  my ($name, $email, $keytype, $keysize, $expire, $pass, $comment) = @_;

  return unless $keysize =~ /$self->{VKEYSZ}/ 
    and $keysize > 767 and $keysize < 4097
      and $pass =~ /$self->{VPASSPHRASE}/

GPG.pm  view on Meta::CPAN

    }
  }
}

sub _exec_gen_key {
  my $self = shift;
  my ($name, $email, $keytype, $keysize, $expire, $pass, $comment, $forked) = @_;

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, '--no-tty', '--status-fd', '1', '--command-fd', 0,
                  '--gen-key'], \$in, \$out, \$err);
  if ($forked) {
    local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';
  }

  pump $h until $out =~ /keygen\.algo/g; $in .= "1\n";
  pump $h until $out =~ /keygen\.size/g; $in .= "$keysize\n";
  pump $h until $out =~ /keygen\.valid/g; $in .= "$expire\n";
  pump $h until $out =~ /keygen\.name/g; $in .= "$name\n";
  pump $h until $out =~ /keygen\.email/g; $in .= "$email\n";

GPG.pm  view on Meta::CPAN

  }	
  print "|\n" if $forked;
  finish $h;
}

sub keydb {
  my $self = shift; 
  my @ids = map { return unless /$self->{VKEYID}/; $_ } @_;
  my @moreopts = qw(--fingerprint --fingerprint --with-colons);
  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  backtick ($self->gpgbin, @opts, '--marginals-needed', $self->marginals, '--check-trustdb');
  my ($keylist) = backtick($self->gpgbin, @opts, '--marginals-needed', $self->marginals,
			   '--no-tty', '--check-sigs', @moreopts, @ids); 
  my ($seclist) = backtick($self->gpgbin, @opts,
			   '--no-tty', '--list-secret-keys', @moreopts, @ids); 
  my @keylist = split /\n(\s*\n)?/, $keylist;
  my @seclist = split /\n(\s*\n)?/, $seclist;
  $self->parsekeys (@keylist, @seclist);
}

sub keyinfo {
  shift->keydb(@_);
}

sub parsekeys {
  my $self=shift; my @keylist = @_;
  my @keys; my ($i, $subkey, $subnum, $uidnum) = (-1);
  my $keyring = '';
  $^W = 0;
  foreach (@keylist) {
    next if /^\-/;
    next if /^(gpg|tru):/;
    if (/^\//) {
      $keyring = $_; chomp $keyring;
      next;
    }
    if (/^(pub|sec)/) {
      $uidnum=-1; $subnum=-1; $subkey=0;
      my ($type, $trust, $size, $algorithm, $id, $created, 
	  $expires, $u2, $ownertrust, $uid) = split (':');
      $keys[++$i] = { 
		     Keyring    =>    $keyring,

GPG.pm  view on Meta::CPAN

  my $self = shift; 

  my ($key, $oldpass, $newpass) = @_;
  return unless $oldpass =~ /$self->{VPASSPHRASE}/ 
    and $newpass =~ /$self->{VPASSPHRASE}/
      and $key->{Type} eq 'sec';

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));

  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, '--no-tty', '--status-fd', '1', '--command-fd', 0,
                 '--edit-key', $key->{ID}], \$in, \$out, \$err, timeout( 30 ));
  local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';

  pump $h until $out =~ /keyedit\.prompt/g; $in .= "passwd\n";
  pump $h until ($out =~ /GOOD_PASSPHRASE/g
		 or $out =~ /(passphrase\.enter)/g);
  
  unless ($1) {
    finish $h, return if $oldpass;
  }

GPG.pm  view on Meta::CPAN

}

sub keytrust {
  my $self = shift; 
  my ($key, $trustlevel) = @_;
  return unless $trustlevel =~ /$self->{VTRUSTLEVEL}/;

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));

  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, '--no-tty',
                 '--status-fd', '1', '--command-fd', 0,
                 '--edit-key', $key->{ID}], 
                 \$in, \$out, \$err, timeout( 30 ));
  local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';
  pump $h until $out =~ /keyedit\.prompt/g; $in .= "trust\n";
  pump $h until $out =~ /edit_ownertrust\.value/g; $in .= "$trustlevel\n";
  if ($trustlevel == 5) {
    pump $h until $out =~ /edit_ownertrust\.set_ultimate\.okay/g; $in .= "Y\n";
  }
  pump $h until $out =~ /keyedit\.prompt/g; $in .= "quit\n";

GPG.pm  view on Meta::CPAN

  # Check if already signed.
  return 1 unless grep { !grep { $signingkey eq $_->{ID} } 
			   @{$_->{Signatures}} } 
    (@{$key->{UIDs}})[@uids];

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));
  push (@opts, '--default-key', $self->secretkey) if $self->secretkey;;

  my ($in, $out, $err, $in_q, $out_q, $err_q);

  my $h = start ([$self->gpgbin, @opts, '--status-fd', '1', '--command-fd', 0, '--no-tty',
                 '--edit-key', $key->{ID}], \$in, \$out, \$err, timeout( 30 ));
  local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';

  for (@uids) {
    my $uid = $_+1;
    pump $h until ($out =~ /keyedit\.prompt/g); 
    $in .= "uid $uid\n"; 
  }
  pump $h until ($out =~ /keyedit\.prompt/g); 
  $out = '';

GPG.pm  view on Meta::CPAN

sub delkey {
  my $self = shift; 
  my $key = shift; 
  return unless $key->{ID} =~ /$self->{VKEYID}/;

  my $del = $key->{Type} eq 'sec' ?
    '--delete-secret-and-public-key':'--delete-key';
  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));

  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, '--no-tty', '--status-fd', '1', '--command-fd', 0,
                 $del, $key->{ID}], \$in, \$out, \$err, timeout( 30 ));
  local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';
  pump $h until ($out =~ /delete it first\./g or $out =~ /(delete_key)(.secret)?.okay/g); 
                       #! ^^^^^^^^^^^^^^^^^ to-fix.
  finish $h, return undef unless $1; 
  $in .= "Y\n";
  if ($key->{Type} eq 'sec') {
    pump $h until $out =~ /delete_key.okay/g; $in .= "Y\n";
  }
  finish $h;

GPG.pm  view on Meta::CPAN

}

sub disablekey {
  my $self = shift; 
  my $key = shift;
  return unless $key->{ID} =~ /$self->{VKEYID}/;

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));

  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, '--no-tty', '--status-fd', '1', '--command-fd', 0,
                 '--edit-key', $key->{ID}], \$in, \$out, \$err, timeout( 30 ));
  local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';
  pump $h until ($out =~ /been disabled/g or $out =~ /(keyedit\.prompt)/g); 
                       #! ^^^^^^^^^^^^^ to-fix.
  finish $h, return undef unless $1; 
  $in .= "disable\n";
  pump $h until $out =~ /keyedit\.prompt/g; $in .= "quit\n";
  finish $h;
  return 1;
}

sub enablekey {
  my $self = shift; 
  my $key = shift;
  return unless $key->{ID} =~ /$self->{VKEYID}/;

  my @opts = (split (/\s+/, "$self->{FORCEDOPTS} $self->{GPGOPTS}"));

  my ($in, $out, $err, $in_q, $out_q, $err_q);
  my $h = start ([$self->gpgbin, @opts, '--no-tty', '--status-fd', '1', '--command-fd', 0,
                 '--edit-key', $key->{ID}], \$in, \$out, \$err, timeout( 30 ));
  local $SIG{CHLD} = 'IGNORE'; local $SIG{PIPE} = 'IGNORE';
  pump $h until ($out =~ /been disabled/g or $out =~ /(keyedit\.prompt)/g); 
                       #! ^^^^^^^^^^^^^ to-fix.
  finish $h, return undef unless $1; 
  $in .= "enable\n";
  pump $h until $out =~ /keyedit\.prompt/g; $in .= "quit\n";
  finish $h;
  return 1;
}

GPG.pm  view on Meta::CPAN


sub debug {
  my $self = shift;
  return $self->{DEBUG} unless defined $_[0];
  unless ($_[0] == $self->{DEBUG}) { $ENV{IPCRUNDEBUG} = $_[0] ? 'data' : ''; }
  $self->{DEBUG} = $_[0];
}

sub AUTOLOAD {
  my $self = shift; (my $auto = $AUTOLOAD) =~ s/.*:://;
  if ($auto =~ /^(passphrase|secretkey|armor|gpgbin|gpgopts|delay|marginals|
                  detach|clearsign|encryptsafe|version|comment|tmpdir|tmpdirs|
                  tmpfiles|tmpsuffix|nofork)$/x) {
    return $self->{"\U$auto"} unless defined $_[0];
    $self->{"\U$auto"} = shift;
  }
  elsif ($auto eq 'DESTROY') {
  }
  else {
    croak "Could not AUTOLOAD method $auto.";
  }

GPG.pm  view on Meta::CPAN

Crypt::GPG - An Object Oriented Interface to GnuPG.

=head1 VERSION

 $Revision: 1.64 $
 $Date: 2007/04/02 13:34:25 $

=head1 SYNOPSIS

  use Crypt::GPG;
  my $gpg = new Crypt::GPG;

  $gpg->gpgbin('/usr/bin/gpg');      # The GnuPG executable.
  $gpg->secretkey('0x2B59D29E');     # Set ID of default secret key.
  $gpg->passphrase('just testing');  # Set passphrase.

  # Sign a message:

  my $sign = $gpg->sign('testing again');

  # Encrypt a message:

  my @encrypted = $gpg->encrypt ('top secret', 'test@bar.com');

  # Get message info:

  my @recipients = $gpg->msginfo($encrypted);

  # Decrypt a message.

  my ($plaintext, $signature) = $gpg->verify($encrypted);

  # Key generation:

  $status = $gpg->keygen 
    ('Test', 'test@foo.com', 'ELG-E', 2048, 0, 'test passphrase');
  print while (<$status>); close $status;

  # Key database manipulation:

  $gpg->addkey($key, @ids);
  @keys = $gpg->keydb(@ids);

  # Key manipulation:

  $key = $keys[0];
 
  $gpg->delkey($key);
  $gpg->disablekey($key);
  $gpg->enablekey($key);
  $gpg->keypass($key, $oldpassphrase, $newpassphrase);
  $keystring = $gpg->export($key);

=head1 DESCRIPTION

The Crypt::GPG module provides access to the functionality of the
GnuPG (www.gnupg.org) encryption tool through an object oriented
interface. 

It provides methods for encryption, decryption, signing, signature
verification, key generation, key certification, export and
import. Key-server access is on the todo list.

This release of the module may create compatibility issues with
previous versions. If you find any such problems, or any bugs or
documentation errors, please do report them to
crypt-gpg at neomailbox.com.

=head1 CONSTRUCTOR

=over 2

=item B<new()>

Creates and returns a new Crypt::GPG object.

=back

=head1 DATA METHODS

=over 2

=item B<gpgbin($path)>

Sets the B<GPGBIN> instance variable which gives the path to the GnuPG
binary.

=item B<gpgopts($opts)>

Sets the B<GPGOPTS> instance variable which may be used to pass
additional options to the GnuPG binary. For proper functioning of this
module, it is advisable to always include '--lock-multiple' in the
GPGOPTS string.

=item B<delay($seconds)>

Sets the B<DELAY> instance variable. This is no longer necessary (nor
used) in the current version of the module, but remains so existing

GPG.pm  view on Meta::CPAN

  - Enabled use of default Key ID for signing

  - Allow for GPG returning 8 or 16 bit KeyIDs (thanks to Roberto Jimenoca)

  - Fixed tempfiles being left around after decrypt()

  - Changed exit() to CORE::exit() (suggested by Jonathan R. Baker)

Revision 1.61  2006/12/21 12:36:28  ashish

  - Skip tests if gpg not found.

  - Use File::Spec to determine tmpdir. Suggested by Craig Manley.

Revision 1.59  2006/12/19 12:51:54  ashish

  - Documentation fixes.

  - Removed tests for obsolete 768 bit keys.

  - Bugfixes.

  - Tested with gpg 1.4.6.

Revision 1.57  2005/12/15 17:09:17  ashish

  - Fixed bug in decrypt

  - Fixed small key certification bugs.

Revision 1.50  2005/02/10 12:32:51  cvs

 - Overhauled to use IPC::Run instead of Expect.

 - Test suite split up into multiple scripts.

Revision 1.42  2002/12/11 03:33:19  cvs

 - Fixed bug in certify() when trying to certify revoked a key.

 - Applied dharris\x40drh.net's patch to allow for varying date formats
   between gpg versions, and fix time parsing and the
   Crypt::GPG::Signature autoloaded accessor functions.

Revision 1.40  2002/09/23 23:01:53  cvs

 - Fixed a bug in keypass()

 - Documentation fixes.

Revision 1.37  2002/09/21 02:37:49  cvs

GPG.pm  view on Meta::CPAN

 - Added certify() method, to enable certifying keys.

 - Added Crypt::GPG::Signature methods - validity(), keyid(), time()
   and trusted().

=back

=head1 AUTHOR

Crypt::GPG is Copyright (c) 2000-2007 Ashish Gulhati
<crypt-gpg at neomailbox.com>. All Rights Reserved.

=head1 ACKNOWLEDGEMENTS

Thanks to Barkha, for inspiration; to the GnuPG team; and to everyone
who writes free software.

=head1 LICENSE

This code is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 BUGS REPORTS, PATCHES, FEATURE REQUESTS

Are very welcome. Email crypt-gpg at neomailbox.com.    

=cut

META.json  view on Meta::CPAN

{
   "abstract" : "An Object Oriented Interface to GnuPG.",
   "author" : [
      "Ashish Gulhati <crypt-gpg@neomailbox.com>"
   ],
   "dynamic_config" : 1,
   "generated_by" : "ExtUtils::MakeMaker version 6.6302, CPAN::Meta::Converter version 2.120921",
   "license" : [
      "unknown"
   ],
   "meta-spec" : {
      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
      "version" : "2"
   },

META.yml  view on Meta::CPAN

---
abstract: 'An Object Oriented Interface to GnuPG.'
author:
  - 'Ashish Gulhati <crypt-gpg@neomailbox.com>'
build_requires:
  ExtUtils::MakeMaker: 0
configure_requires:
  ExtUtils::MakeMaker: 0
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 6.6302, CPAN::Meta::Converter version 2.120921'
license: unknown
meta-spec:
  url: http://module-build.sourceforge.net/META-spec-v1.4.html
  version: 1.4

Makefile.PL  view on Meta::CPAN

use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile (
	       'NAME'	      =>   'Crypt::GPG',
	       'AUTHOR'        =>  'Ashish Gulhati <crypt-gpg@neomailbox.com>',
	       'ABSTRACT_FROM' =>  'GPG.pm',
	       'VERSION_FROM'  =>  'GPG.pm',
	       'PREREQ_PM'     =>  {
				    'Carp'         =>  0,
				    'Fcntl'        =>  0,
				    'IPC::Run'     =>  0,
				    'File::Path'   =>  0,
				    'File::Temp'   =>  0,
				    'Date::Parse'  =>  0,
				   },

README  view on Meta::CPAN

NAME
    Crypt::GPG - An Object Oriented Interface to GnuPG.

VERSION
     $Revision: 1.64 $
     $Date: 2007/04/02 13:34:25 $

SYNOPSIS
      use Crypt::GPG;
      my $gpg = new Crypt::GPG;

      $gpg->gpgbin('/usr/bin/gpg');      # The GnuPG executable.
      $gpg->secretkey('0x2B59D29E');     # Set ID of default secret key.
      $gpg->passphrase('just testing');  # Set passphrase.

      # Sign a message:

      my $sign = $gpg->sign('testing again');

      # Encrypt a message:

      my @encrypted = $gpg->encrypt ('top secret', 'test@bar.com');

      # Get message info:

      my @recipients = $gpg->msginfo($encrypted);

      # Decrypt a message.

      my ($plaintext, $signature) = $gpg->verify($encrypted);

      # Key generation:

      $status = $gpg->keygen 
        ('Test', 'test@foo.com', 'ELG-E', 2048, 0, 'test passphrase');
      print while (<$status>); close $status;

      # Key database manipulation:

      $gpg->addkey($key, @ids);
      @keys = $gpg->keydb(@ids);

      # Key manipulation:

      $key = $keys[0];
 
      $gpg->delkey($key);
      $gpg->disablekey($key);
      $gpg->enablekey($key);
      $gpg->keypass($key, $oldpassphrase, $newpassphrase);
      $keystring = $gpg->export($key);

DESCRIPTION
    The Crypt::GPG module provides access to the functionality of the GnuPG
    (www.gnupg.org) encryption tool through an object oriented interface.

    It provides methods for encryption, decryption, signing, signature
    verification, key generation, key certification, export and import.
    Key-server access is on the todo list.

    This release of the module may create compatibility issues with previous
    versions. If you find any such problems, or any bugs or documentation
    errors, please do report them to crypt-gpg at neomailbox.com.

CONSTRUCTOR
    new()
      Creates and returns a new Crypt::GPG object.

DATA METHODS
    gpgbin($path)
      Sets the GPGBIN instance variable which gives the path to the GnuPG
      binary.

    gpgopts($opts)
      Sets the GPGOPTS instance variable which may be used to pass
      additional options to the GnuPG binary. For proper functioning of this
      module, it is advisable to always include '--lock-multiple' in the
      GPGOPTS string.

    delay($seconds)
      Sets the DELAY instance variable. This is no longer necessary (nor
      used) in the current version of the module, but remains so existing
      scripts don't break.

README  view on Meta::CPAN

        - Enabled use of default Key ID for signing

        - Allow for GPG returning 8 or 16 bit KeyIDs (thanks to Roberto Jimenoca)

        - Fixed tempfiles being left around after decrypt()

        - Changed exit() to CORE::exit() (suggested by Jonathan R. Baker)

      Revision 1.61 2006/12/21 12:36:28 ashish

        - Skip tests if gpg not found.

        - Use File::Spec to determine tmpdir. Suggested by Craig Manley.

      Revision 1.59 2006/12/19 12:51:54 ashish

        - Documentation fixes.

        - Removed tests for obsolete 768 bit keys.

        - Bugfixes.

        - Tested with gpg 1.4.6.

      Revision 1.57 2005/12/15 17:09:17 ashish

        - Fixed bug in decrypt

        - Fixed small key certification bugs.

      Revision 1.50 2005/02/10 12:32:51 cvs

       - Overhauled to use IPC::Run instead of Expect.

       - Test suite split up into multiple scripts.

      Revision 1.42 2002/12/11 03:33:19 cvs

       - Fixed bug in certify() when trying to certify revoked a key.

       - Applied dharris\x40drh.net's patch to allow for varying date formats
         between gpg versions, and fix time parsing and the
         Crypt::GPG::Signature autoloaded accessor functions.

      Revision 1.40 2002/09/23 23:01:53 cvs

       - Fixed a bug in keypass()

       - Documentation fixes.

      Revision 1.37 2002/09/21 02:37:49 cvs

README  view on Meta::CPAN

         selectively import only requested key IDs from a key block.

       - parsekeys() now also figures out which keyring a key belongs to.

       - Added certify() method, to enable certifying keys.

       - Added Crypt::GPG::Signature methods - validity(), keyid(), time()
         and trusted().

AUTHOR
    Crypt::GPG is Copyright (c) 2000-2007 Ashish Gulhati <crypt-gpg at
    neomailbox.com>. All Rights Reserved.

ACKNOWLEDGEMENTS
    Thanks to Barkha, for inspiration; to the GnuPG team; and to everyone
    who writes free software.

LICENSE
    This code is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.

BUGS REPORTS, PATCHES, FEATURE REQUESTS
    Are very welcome. Email crypt-gpg at neomailbox.com.

t/01-keygen.t  view on Meta::CPAN

# -*-cperl-*-
#
# keygen.t - Crypt::GPG key generation tests.
# Copyright (c) 2005-2006 Ashish Gulhati <crypt-gpg at neomailbox.com>
#
# All rights reserved. This code is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: 01-keygen.t,v 1.9 2006/12/21 12:36:35 ashish Exp $

use strict;
use Test;
use Crypt::GPG;

BEGIN { plan tests => 2 }

print STDERR <<__ENDMSG;


NOTE: If the tests are skipped, you may need to install gpg,
      and/or set the environment variable GPGBIN to the 
      location of the gpg binary.

      If the keygen test takes a long time you may need to 
      generate more randomness on your computer (by running a 
      recursive directory listing in the background, for 
      example).

__ENDMSG

my $debug = 0;
my $dir = $0 =~ /^\// ? $0 : $ENV{PWD} . '/' . $0; $dir =~ s/\/[^\/]*$//;
$ENV{HOME} = $dir;

# Create new Crypt::GPG object

my $gpg = new Crypt::GPG;
$ENV{GPGBIN} and $gpg->gpgbin($ENV{GPGBIN});

my $nogpg = 1 unless (-e $gpg->gpgbin);

$gpg->gpgopts('--compress-algo 1 --cipher-algo cast5 --force-v3-sigs --no-comment');
$gpg->debug($debug);

# Start test loop with different key sizes/types
################################################
for my $bits (qw(1024 2048)) {
  for my $type ('ELG-E') {

    # Generate key pair
    #####################
    skip($nogpg,
	 sub {
	   my $status = $gpg->keygen("A $bits $type", "$bits$type\@test.com", 
				     $type, $bits, 0, "$bits Bit $type Test Key");
	   return 0 unless $status;
	   $|=1;
	   while (<$status>) {
	     chomp; print;
	   }
	   close $status; print "\n"; $|=0;
	 }, 0);
  }
}

t/02-import.t  view on Meta::CPAN

# -*-cperl-*-
#
# import.t - Crypt::GPG key import tests.
# Copyright (c) 2005-2006 Ashish Gulhati <crypt-gpg at neomailbox.com>
#
# All rights reserved. This code is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: 02-import.t,v 1.4 2006/12/21 12:36:35 ashish Exp $

use strict;
use Test;
use Crypt::GPG;

BEGIN { plan tests => 1 }

my $debug = 0;
my $dir = $0 =~ /^\// ? $0 : $ENV{PWD} . '/' . $0; $dir =~ s/\/[^\/]*$//;
$ENV{HOME} = $dir;

# Create new Crypt::GPG object

my $gpg = new Crypt::GPG;
$ENV{GPGBIN} and $gpg->gpgbin($ENV{GPGBIN});

my $nogpg = 1 unless (-e $gpg->gpgbin);

$gpg->gpgopts('--compress-algo 1 --cipher-algo cast5 --force-v3-sigs --no-comment');
$gpg->debug($debug);

my @samplekeys; samplekeys();

# Import sample keys
####################
skip($nogpg,
     sub {
       for my $x (@samplekeys) {
	 my ($imported) = $gpg->addkey($x->{Key});
	 return 0 unless $imported->{ID} eq $x->{ID};
       }
       1;
     }
    );

sub samplekeys {
  push (@samplekeys, {'ID' => 'D354E162BCA6DBD1',
		      'Key' => <<__ENDKEY
-----BEGIN PGP PUBLIC KEY BLOCK-----

t/03-export.t  view on Meta::CPAN

# -*-cperl-*-
#
# export.t - Crypt::GPG key export tests.
# Copyright (c) 2005-2006 Ashish Gulhati <crypt-gpg at neomailbox.com>
#
# All rights reserved. This code is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: 03-export.t,v 1.6 2006/12/21 12:36:35 ashish Exp $

use strict;
use Test;
use Crypt::GPG;

BEGIN { plan tests => 10 }

my $debug = 0;
my $dir = $0 =~ /^\// ? $0 : $ENV{PWD} . '/' . $0; $dir =~ s/\/[^\/]*$//;
$ENV{HOME} = $dir;

# Create new Crypt::GPG object

my $gpg = new Crypt::GPG;
$ENV{GPGBIN} and $gpg->gpgbin($ENV{GPGBIN});

my $nogpg = 1 unless (-e $gpg->gpgbin);

$gpg->gpgopts('--compress-algo 1 --cipher-algo cast5 --force-v3-sigs --no-comment');
$gpg->debug($debug);

# Start test loop with different key sizes/types
################################################
for my $bits (qw(1024 2048)) {
  for my $type ('ELG-E') {

    # Export our public key
    #######################
    my $publickey; my $pub;

    skip($nogpg,
	 sub {
	   ($publickey) = grep { $_->{Type} =~ /^pub[^\@]?/ } $gpg->keyinfo("A $bits $type");
	   $pub = $gpg->export($publickey);
	 });

    # Pretend import public key
    ###########################
    skip($nogpg,
	 sub {
	   my ($imported) = $gpg->addkey($pub, 1);
	   $publickey->{ID} eq $imported->{ID};
	 });
    
    # Really import public key
    ##########################
    skip($nogpg,
	 sub {
	   my ($imported) = $gpg->addkey($pub);
	   $publickey->{ID} eq $imported->{ID};
	 });
    
    # Export secret key
    ###################
    my $secretkey; my $sec;
    
    skip($nogpg,
	 sub {
	   ($secretkey) = grep { $_->{Type} =~ /^sec[^\@]?/ } $gpg->keyinfo("A $bits $type");
	   $sec = $gpg->export($secretkey);
	 });
    
    # Import secret key
    ###################
    skip(1, sub {
	   $gpg->addkey($sec);
	 });
  }
}

t/04-encdec.t  view on Meta::CPAN

# -*-cperl-*-
#
# enc-dec.t - Crypt::GPG encryption / decryption tests.
# Copyright (c) 2005-2006 Ashish Gulhati <crypt-gpg at neomailbox.com>
#
# All rights reserved. This code is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: 04-encdec.t,v 1.8 2006/12/21 12:36:35 ashish Exp $

use strict;
use Test;
use Crypt::GPG;

BEGIN { plan tests => 10 }

my $debug = 0;
my $dir = $0 =~ /^\// ? $0 : $ENV{PWD} . '/' . $0; $dir =~ s/\/[^\/]*$//;
$ENV{HOME} = $dir;

# Create new Crypt::GPG object

my @x;
my $gpg = new Crypt::GPG;
$ENV{GPGBIN} and $gpg->gpgbin($ENV{GPGBIN});

my $nogpg = 1 unless (-e $gpg->gpgbin);

$gpg->gpgopts('--compress-algo 1 --cipher-algo cast5 --force-v3-sigs --no-comment');
$gpg->debug($debug);

# Start test loop with different key sizes/types
################################################
for my $bits (qw(1024 2048)) {
  for my $type ('ELG-E') {

    my $secretkey; 
    ($secretkey) = grep { $_->{Type} =~ /^sec[^\@]?/ } $gpg->keyinfo("A $bits $type")
      unless $nogpg;
    $gpg->secretkey($secretkey);
    $gpg->encryptsafe(0);

    # Encrypt
    #########
    skip($nogpg,
	 sub {
	   @x = $gpg->encrypt("Test\n", "A $bits $type");
	 });
    
    for my $nopass (0,1) {
      if ($nopass) {
	# Blank out the Key password and do another round of tests
        ##########################################################
	skip($nogpg,
	     sub {
	       $gpg->passphrase('');
	       $gpg->keypass($secretkey, "$bits Bit $type Test Key", '');
	     });
      }
      
      # Decrypt
      #########
      skip($nogpg,
	   sub {
	     $gpg->passphrase($nopass ? '' : "$bits Bit $type Test Key");
	     my ($clear) = $gpg->decrypt(@x);
	     defined $clear and $clear eq "Test\n";
	   });
    }
    
    # Set passphrase back to original
    #################################
    skip($nogpg,
	 sub {
	   $gpg->keypass($secretkey, '', "$bits Bit $type Test Key");
	 });
  }
}

t/05-sigver.t  view on Meta::CPAN

# -*-cperl-*-
#
# sigver.t - Crypt::GPG signing / verification tests.
# Copyright (c) 2005-2006 Ashish Gulhati <crypt-gpg at neomailbox.com>
#
# All rights reserved. This code is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: 05-sigver.t,v 1.7 2006/12/21 12:36:35 ashish Exp $

use strict;
use Test;
use Crypt::GPG;

BEGIN { plan tests => 32 }

my $debug = 0;
my $dir = $0 =~ /^\// ? $0 : $ENV{PWD} . '/' . $0; $dir =~ s/\/[^\/]*$//;
$ENV{HOME} = $dir;

# Create new Crypt::GPG object

my @x;
my $gpg = new Crypt::GPG;
$ENV{GPGBIN} and $gpg->gpgbin($ENV{GPGBIN});

my $nogpg = 1 unless (-e $gpg->gpgbin);

$gpg->gpgopts('--compress-algo 1 --cipher-algo cast5 --force-v3-sigs --no-comment');
$gpg->debug($debug);

# Start test loop with different key sizes/types
################################################
for my $bits (qw(1024 2048)) {
  for my $type ('ELG-E') {

    my $secretkey;
    ($secretkey) = grep { $_->{Type} =~ /^sec[^\@]?/ } $gpg->keyinfo("A $bits $type")
      unless $nogpg;
    $gpg->secretkey($secretkey->{ID});
    
    for my $nopass (0,1) {
      if ($nopass) {
	# Blank out the Key password and do another round of tests
        ##########################################################
	skip($nogpg,
	     sub {
	       $gpg->passphrase('');
	       $gpg->keypass($secretkey, "$bits Bit $type Test Key", '');
	     });
      }
      
      $gpg->passphrase("$bits Bit $type Test Key") unless $nopass;
      $gpg->encryptsafe(0); #! Must test with both trusted and untrusted keys.

      # Encrypt and sign with GPG
      ###########################
      my @xs;
      skip($nogpg,
	   sub {
	     @xs = $gpg->encrypt("Test\n", "A $bits $type", '-sign');
	   });
      
      # Sign with GPG
      ###############
      #! Need to check for hang when secret key not set.
      skip($nogpg,
	   sub {
	     my $signed = $gpg->sign("Signing a test\nmessage, combining\nand\r\nline endings.\n");
	     $signed =~ /^-----BEGIN PGP SIGNATURE-----.*-----END PGP SIGNATURE-----$/s;
	   });
      
      #! Clearsign with GPG
      #####################
      skip(sub {1});
    
      #! Detached sign with GPG
      #########################
      skip(sub {1});
      
      # Decrypt & Verify GPG with GPG
      ###############################
      skip($nogpg,
	   sub {
	     $gpg->secretkey($secretkey);
	     my ($clear, $sign) = $gpg->decrypt(@xs);
	     defined $clear and $clear eq "Test\n" 
	       and ref($sign) eq 'Crypt::GPG::Signature';
	   });
      
      #! Verify Signature (GPG with GPG)
      ##################################
      skip(sub {1});
    
      #! Verify detached signature (GPG with GPG)
      #################################################
      skip(sub {1});
    }
    # Set passphrase back to original
    #################################
    skip($nogpg,
	 sub {
	   $gpg->keypass($secretkey, '', "$bits Bit $type Test Key");
	 });
  }
}

t/06-keyops.t  view on Meta::CPAN

# -*-cperl-*-
#
# keyops.t - Crypt::GPG key manipulation tests.
# Copyright (c) 2005-2006 Ashish Gulhati <crypt-gpg at neomailbox.com>
#
# All rights reserved. This code is free software; you can
# redistribute it and/or modify it under the same terms as Perl
# itself.
#
# $Id: 06-keyops.t,v 1.5 2006/12/21 12:36:35 ashish Exp $

use strict;
use Test;
use Crypt::GPG;

t/06-keyops.t  view on Meta::CPAN


my $debug = 0;
my $dir = $0 =~ /^\// ? $0 : $ENV{PWD} . '/' . $0; $dir =~ s/\/[^\/]*$//;
$ENV{HOME} = $dir;

my @samplekeys; samplekeys();

# Create new Crypt::GPG object

my @x;
my $gpg = new Crypt::GPG;
$ENV{GPGBIN} and $gpg->gpgbin($ENV{GPGBIN});

my $nogpg = 1 unless (-e $gpg->gpgbin);

$gpg->gpgopts('--compress-algo 1 --cipher-algo cast5 --force-v3-sigs --no-comment');
$gpg->debug($debug);

unless ($nogpg) {
  for my $x (@samplekeys) {
    my ($imported) = $gpg->addkey($x->{Key});
    return 0 unless $imported->{ID} eq $x->{ID};
  }
}

# Start test loop with different key sizes/types
################################################
for my $bits (qw(1024 2048)) {
  for my $type ('ELG-E') {

    my @mykeys; @mykeys = $gpg->keyinfo("A $bits $type") unless $nogpg;
    my ($publickey) = grep { $_->{Type} =~ /^pub[^\@]?/ } @mykeys;
    my ($secretkey) = grep { $_->{Type} =~ /^sec[^\@]?/ } @mykeys;
    $gpg->secretkey($secretkey->{ID});
    
    for my $nopass (0,1) {
      if ($nopass) {
	# Blank out the Key password and do another round of tests
        ##########################################################
	skip($nogpg,
	     sub {
	       $gpg->passphrase('');
	       $gpg->keypass($secretkey, "$bits Bit $type Test Key", '');
	     });
      }
      
      $gpg->passphrase("$bits Bit $type Test Key") unless $nopass;
      $gpg->encryptsafe(0); #! Must test with both trusted and untrusted keys.

      # Local-sign all sample public keys
      ###################################
      #! Test check for already signed.
      #! Test check for UID out of range. It's broken.
      skip($nogpg,
	   sub {
	     for my $x (@samplekeys) {
	       return unless $gpg->certify($x->{ID}, 1, 0, 0);
	     }
	     1;
	   });
      
      # Sign all sample public keys
      #############################
      skip($nogpg,
	   sub {
	     for my $x (@samplekeys) {
	       return unless $gpg->certify($x->{ID}, 0, 0, 0);
	     }
	     1;
	   });
      
      #! Verify key signatures
      ########################
      skip(sub {1});

      # Change key trust
      ##################
      skip($nogpg,
	   sub {
	     $gpg->keytrust($publickey, 3);
	   });
      
      # Disable key
      #############
      skip($nogpg,
	   sub {
	     $gpg->disablekey($publickey);
	   });
      
      # Enable key
      ############
      skip($nogpg,
	   sub {
	     $gpg->enablekey($publickey);
	   });
      
    }

    # Set passphrase back to original
    #################################
    skip($nogpg,
	 sub {
	   $gpg->keypass($secretkey, '', "$bits Bit $type Test Key");
	 });
    
    # Delete GPG key pair
    #####################
    skip($nogpg,
	 sub {
	   $gpg->delkey($secretkey);
	 });
  }
}

sub samplekeys {
  push (@samplekeys, 
	{'ID' => '143C9F41D8F056DD',
	 'Key' => <<__ENDKEY
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.2.4



( run in 1.579 second using v1.01-cache-2.11-cpan-df04353d9ac )