I’ve been working on a project at work dealing with PGP. Basically, we receive some sensitive information from a business partner, and it needs to be secure. They were considering setting up a rather expensive solution involving leased communication lines and virtual private networking. But I happened to mention that we could automate some of it with PGP, and in an unusual fit of common sense, they decided to let me have a go at it.
So, I got our Network Administrator to set up a new mailbox for the PGP ID, got our PGP keypair generated, got our business partner to give us a test file encrypted to our public key, and I was ready to begin the coding phase. I’m using ActivePerl under Win32 for this project, because, well, that’s what we have handy. I did express to my manager at one point that it would probably have gone faster and easier under Linux, and he seemed open to that idea. But I pursued the solution under Windows first, in the interest of conserving resources.
But, I kept running into problems. Most of the Perl modules for dealing with PGP have XS components. Which means that they include some C code that must be compiled to the installation target. Normally (i.e. under Linux or FreeBSD) this is not a problem — the C compiler and linkers and such are already on the system, and the installation script will automagically locate them, compile the C code, link it to the perl glue, and you’re up and running. Under Windows, it’s not always so easy. Sometimes it can be, but as it turns out, most of the modules I tried had unix-specific dependencies which I didn’t have the time or knowledge-base to overcome. I was just about to give up on Windows and start setting up a Linux box, but I decided to try one last Perl module: Crypt::PGPSimple.
As the name implies, PGPSimple is very simple. It’s nothing more than an object oriented interface that calls out to the command-line PGP tools. You tell it where to find the PGP executable, where to find your private key, give it some encrypted data, and it spits out the decrypted data. Except, I couldn’t get it to spit out the decrypted data. Instead, it kept spitting out the first several bytes of the encrypted data.
“What the?” I scratched my head for a bit. Why in the world would I be getting part of my input back as output? I groveled through the PGPSimple source. Yup, it’s pretty simple, alright…. I enabled some debugging code. I disabled the code that deleted the temporary files. I checked the captured output from the execution of the actual PGP command. Nothing unsual there, everything looked fine. I reverse engineered the PGP command line that the perl program was executing (not hard to reverse-engineer, because the module is simple, right?). I simulated the command manually from a command line….
C:> type test.txt.pgp | pgp -f +batchmode +force > output.txt
Voila, unencrypted text!
“What the?!?” Okay, how come this command works from the command line, but not from inside the Perl module. I stared at the code. I stared at it until beads of sweat formed on my brow. I stared until that code finally flinched. “Aha!” The PGPSimple module works by executing PGP in “filter” mode, passing data through the STDIN and STDOUT filehandles. This is a common method for filtering text. Text. But waitaminute…. I’m dealing with a binary file. Hmmmm….
It seems that the writer of the PGPSimple module assumed that the encrypted input would always be in ASCII-armored format, and opened the input file handle normally. Perl usually assumes that it’s dealing with text files. That’s Perl’s strength — mangling text data until you get it like you like it. To handle binary files, the file handle must be explicitly set to binary mode. The fix is simple. Edit the PGPSimple.pm file in a text editor, and look for the following section of code in the DoPgpCommand method:
# execute PGP command open (PGPCOMMAND, "|$pgp_command"); binmode PGPCOMMAND; ## <-- ADD THIS print PGPCOMMAND $pgp_args; close (PGPCOMMAND);
Add the “binmode” line as indicated in the comment above, and you can now handle binary files. If only all program fixes were this…simple.