2

I am struggling to change a GnuPG private key passphrase with a script.

This is as far as I get: it returns that the process was successful at the end, but it doesn't change the passphrase. Any other form of options to gpg result in errors and no passphrase change.

Anyone have any hints? I'll accept answers using Symfony's process component or without - I just need to get it working.

Python/Bash/Expect answers also welcome. It should work without exposing the passphrase in the command line.

use Symfony\Component\Process\InputStream;
use Symfony\Component\Process\Process;

$fingerprint = '0123456789ABCDEF';
$previous = 'secret';
$passphrase = 'moresecret';

$process = new Process(
    [
        'gpg',
        '--passphrase-fd=0',
        '--pinentry-mode=loopback',
        '--change-passphrase',
        $fingerprint,
    ]
);

$input = new InputStream();
$process->setInput($input);

$process->start();

$input->write($previous."\n");
$input->write($passphrase."\n");
$input->close();

foreach ($process as $type => $data) {
    if ($process::OUT === $type) {
        echo "\nSTDOUT: $data";
    } else {
        echo "\nSTDERR: $data";
    }
}

return $process->isSuccessful();
datashaman
  • 8,301
  • 3
  • 22
  • 29

1 Answers1

0

I managed to get this working with expect and the Symfony Process component.

Save this expect script on the filesystem:

#!/usr/bin/expect -f

spawn gpg --pinentry-mode=loopback --change-passphrase $env(GPG_USERID)
set timeout -1
expect "Enter passphrase: " { send "$env(GPG_PREVIOUS)\r" }
expect "Enter passphrase: " { send "$env(GPG_PASSPHRASE)\r" }
interact

Use the following code to execute and analyse the result of the script run:

use Symfony\Component\Process\Process;

$userid = 'user@example.com';
$previous = 'secret';
$passphrase = 'moresecret';

$path = realpath(__DIR__ . '/path/to/your/script.exp');

$process = new Process(
    [
        $path,
    ],
    null,
    [
        'GPG_USERID' => $userid,
        'GPG_PREVIOUS' => $previous,
        'GPG_PASSPHRASE' => $passphrase,
    ]
);

$process->start();

$success = true;

$process->wait(function ($type, $buffer) use (&$success) {
    if (
        Process::OUT === $type
        && strpos($buffer, 'Bad passphrase') !== false
    ) {
        $success = false;
    }
});

return $process->isSuccessful() && $success;

gnupg returns exit code 0 even when the previous passphrase is entered incorrectly. It reports this on stdout, not stderr.

I parse the output looking for Bad passphrase and incorporate that into the success check at the end.

I now have a perfectly scripted change passphrase method, without compromising security by using command-line parameters.

datashaman
  • 8,301
  • 3
  • 22
  • 29