riven has asked for the wisdom of the Perl Monks concerning the following question:

Greetings, monks.

I was writing an instant messaging client for fun, which would encrypt messages with gpg before sending them across the network using netcat (there are likely better ways of doing this than netcat, but it's the one I'm used to). However, when I try to encrypt my message, I get the response:

wrong response from gnupg (expected SHM_INFO): at ./pchat line (I marked the line below)

It's my understanding that the GPG module expects filenames as arguments for the messages to encrypt, and where to save the result, and it returns SHM_INFO upon file-not-found errors. I thought I could use local variables as if they were files by passing them in like \$varname, but this appears not to be working correctly.

Any help would be greatly appreciated. Thanks!

#!/usr/bin/env perl use warnings; use strict; use Term::ReadKey; # For the noecho use GnuPG qw( :algo ); # From the GnuPG CPAN page if( $ARGV != 2 ) { die "Usage: pchat <host> <port>"; } client( $ARGV[0], $ARGV[1] ); sub client { my $target = shift; my $port = shift; print "Who do I encrypt to: "; my $keyname = <STDIN>; chomp( $keyname ); ReadMode('noecho'); print "PGP Key passphrase (not echoed): "; my $passphrase = <STDIN>; chomp $passphrase; print "\n"; # The user's return key won't be echoed ReadMode(0); # Reset term status open (my $net, "|-", "nc $target $port") or die "Can't open ne +tcat!"; while( 0 == 0 ) { my $message = encrypt( $keyname, $passphrase ); last if $message eq "exit"; print $message . "\n"; # Here's where I'm _going_ to send the message via net +cat. # I haven't done it yet, I want to just get the encryp +tion working first. } close $net; print "Connection Terminated.\n"; } sub encrypt { my $gpg = new GnuPG() or die "Cannot open gpg!"; my $keyname = shift; my $passphrase = shift; print "Message: "; my $message = <STDIN>; chomp( $message ); return "exit" if( length($message) == 0 ); # Quit if blank mes +sage $message . "\n" . chr(04); # Tack on a return and EOF, so this + looks like a file to GPG my $encrypted_message = ""; $gpg->encrypt( recipient => $keyname, plaintext => \$message, +output => \$encrypted_message, sign => 1, passphrase => $passphrase, +armor => 1 ) or die "Error encrypting: $!"; # The above is the line I'm getting errors on. return $encrypted_message; }

Replies are listed 'Best First'.
Re: Perl GPG Chat
by atcroft (Abbot) on Apr 12, 2012 at 02:27 UTC

    GnuPG::Tie::Encrypt from GNUPG, from the given example, would appear to be more what you might be looking for. It uses a tied file handle to interface with GnuPG, rather than requiring data to be written to a file to be encrypted.

    Hope that helps.

      Thank you so much, that does exactly what I want! I'm much farther along now, and sending messages works fine. However, on the receiving end when I try to decrypt the message I get: protocol error: expected DECRYPTION_OKAY got PLAINTEXT

      I can't find where I've messed up, I tried to follow the example CPAN code as much as I could - but clearly I've done something wrong. I've marked the line near the very bottom where it appears to be erroring.

      #!/usr/bin/env perl use warnings; use strict; use Term::ReadKey; # For the noecho use GnuPG::Tie::Encrypt; use GnuPG::Tie::Decrypt; # Global vars here my $message_separator = "!"; # Separate messages on !, because gpg wil +l put newlines into the message, but never uses '!' in ascii-armor # End of global vars client( $ARGV[0], $ARGV[1] ) if( @ARGV == 2 ); daemon( $ARGV[0] ) if( @ARGV == 1 ); usage() if( @ARGV != 1 && @ARGV != 2 ); sub usage { print "Usage:\n"; print "$0 <port_number> # For listening as a daemon\n"; print "$0 <ip_address> <port_number> # For connecting to other ser +vers\n"; } sub client { my $target = shift; my $port = shift; my $status = `nc -z $target $port` or die "Can't open data connect +ion (port closed or host unreachable)"; print "Who do I encrypt to: "; my $keyname = <STDIN>; chomp( $keyname ); ReadMode('noecho'); print "PGP Key passphrase (not echoed): "; my $passphrase = <STDIN>; chomp $passphrase; print "\n"; # The user's return key won't be echoed ReadMode(0); # Reset term status open (NET, "|-", "nc $target $port") or die "Can't open netcat: $! +"; while( 0 == 0 ) { my $message = encrypt( $keyname, $passphrase ); last if $message eq "exit"; select(NET); # We select NET so $| will effect it $| = 1; # We can't buffer because of interactions with netcat. print $message . $message_separator; # Send it through the tub +es! } close NET; select(STDOUT); print "Connection Terminated.\n"; } sub encrypt { select(STDOUT); my $keyname = shift; my $passphrase = shift; print "Message: "; my $message = <STDIN>; chomp( $message ); return "exit" if( length($message) == 0 ); # Quit if blank message tie *ENCRYPT, 'GnuPG::Tie::Encrypt', recipient => $keyname, sign = +> 1, passphrase => $passphrase, armor => 1; print ENCRYPT $message; local $/ = undef; # Don't stop reading from ENCRYPT at a newline l +ike normal my $encrypted_message = <ENCRYPT>; close ENCRYPT; untie *ENCRYPT; return $encrypted_message; } sub daemon # Listen for incomming connections { my $port = shift or die "Usage: $0 <port_number>"; ReadMode('noecho'); print "PGP Key passphrase (not echoed): "; my $passphrase = <STDIN>; chomp $passphrase; print "\n"; # The user's return key won't be echoed ReadMode(0); # Reset term status print( msg_time(), " Server started.\n"); open (NET, "-|", "nc -k -l $port" ) or die "Can't open netcat!"; local $/ = $message_separator; # Seperate off chosen symbol instea +d of newline while( <NET> ) { my $plaintext = decrypt($passphrase, $_); print( msg_time(), " ", $plaintext); } close NET; } sub msg_time { my( $hours, $minutes, $seconds ) = (localtime)[2,1,0]; return "$hours:$minutes:$seconds"; } sub decrypt { my $passphrase = shift; my $message = shift; tie *DECRYPT, 'GnuPG::Tie::Decrypt', passphrase => $passphrase; print DECRYPT $message; # Here's where I error out my $plaintext_message = <DECRYPT>; close DECRYPT; untie *DECRYPT; return $plaintext_message; }