Skip to content

Commit

Permalink
Refactor gpg import to use machine readable colon format
Browse files Browse the repository at this point in the history
Signed-off-by: Aleksei Khudiakov <aleksey@xerkus.pro>
  • Loading branch information
Xerkus committed Feb 12, 2024
1 parent 3f7565f commit 5db5062
Show file tree
Hide file tree
Showing 7 changed files with 401 additions and 7 deletions.
61 changes: 54 additions & 7 deletions src/Gpg/ImportGpgKeyFromStringViaTemporaryFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,76 @@

namespace Laminas\AutomaticReleases\Gpg;

use Laminas\AutomaticReleases\Gpg\Value\ColonFormattedKeyRecord;
use Psl;
use Psl\Env;
use Psl\Filesystem;
use Psl\Regex;
use Psl\Shell;
use Psl\Str;
use Psl\Vec;

use function array_shift;
use function count;
use function Psl\File\write;

final class ImportGpgKeyFromStringViaTemporaryFile implements ImportGpgKeyFromString
{
public function __invoke(string $keyContents): SecretKeyId
{
$keyFileName = Filesystem\create_temporary_file(Env\temp_dir(), 'imported-key');
write($keyFileName, $keyContents);
try {
write($keyFileName, $keyContents);

$output = Shell\execute('gpg', ['--import', $keyFileName], null, [], Shell\ErrorOutputBehavior::Append);
$output = Shell\execute(
'gpg',
['--import', '--import-options', 'import-show', '--with-colons', $keyFileName],
null,
[],
Shell\ErrorOutputBehavior::Discard,
);

$matches = Regex\first_match($output, '/key\\s+([A-F0-9]+):\\s+secret\\s+key\\s+imported/im', Regex\capture_groups([1]));
$keyRecords = Vec\filter_nulls(Vec\map(
Str\split($output, "\n"),
static fn (string $record): ColonFormattedKeyRecord|null => ColonFormattedKeyRecord::fromRecordLine(
$record,
),
));

Psl\invariant($matches !== null, 'unexpected output.');
// Primary key secret is exported as unusable gnu-stub secret with --export-secret-subkeys.
// Consequently primary key always has secret even when signing is done by subkey with actual secret.
$primaryKeyRecords = Vec\filter(
$keyRecords,
static fn (ColonFormattedKeyRecord $record): bool => $record->isPrimaryKey() && $record->hasSecretKey(),
);

Filesystem\delete_file($keyFileName);
Psl\invariant(count($primaryKeyRecords) > 0, 'Imported GPG key material does not contain secret key');
// import can contain multiple keys. Sanity check to ensure no unexpected key usage.
Psl\invariant(
count($primaryKeyRecords) === 1,
'Imported GPG key material contains more than one primary key',
);

return SecretKeyId::fromBase16String($matches[1]);
$primaryKeyRecord = array_shift($primaryKeyRecords);

if ($primaryKeyRecord->hasSignCapability()) {
return $primaryKeyRecord->keyId();
}

$subkeyRecords = Vec\filter(
$keyRecords,
static function (ColonFormattedKeyRecord $record): bool {
return $record->isSubkey() && $record->hasSecretKey() && $record->hasSignCapability();
},
);

Psl\invariant(
count($subkeyRecords) > 0,
'Imported GPG key material does not contain primary key or subkey with sign capabilities',
);

return $primaryKeyRecord->keyId();
} finally {
Filesystem\delete_file($keyFileName);
}
}
}
67 changes: 67 additions & 0 deletions src/Gpg/Value/ColonFormattedKeyRecord.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Laminas\AutomaticReleases\Gpg\Value;

use Laminas\AutomaticReleases\Gpg\SecretKeyId;
use Psl\Str;

use function in_array;
use function str_contains;

final readonly class ColonFormattedKeyRecord
{
private const FIELD_TYPE = 0;
private const FIELD_KEYID = 4;
private const FIELD_CAPABILITIES = 11;

private function __construct(
private bool $isSubkey,
private bool $hasSecretKey,
private SecretKeyId $keyId,
private string $capabilities,
) {
}

public static function fromRecordLine(string $recordLine): self|null
{
$record = Str\split($recordLine, ':');
$type = $record[self::FIELD_TYPE] ?? '';
if (! in_array($type, ['pub', 'sec', 'sub', 'ssb'])) {
return null;
}

$isSubkey = in_array($type, ['sub', 'ssb']);
$hasSecretKey = in_array($type, ['sec', 'ssb']);
$keyId = SecretKeyId::fromBase16String($record[self::FIELD_KEYID] ?? '');
$capabilities = $record[self::FIELD_CAPABILITIES] ?? '';

return new self($isSubkey, $hasSecretKey, $keyId, $capabilities);
}

public function isPrimaryKey(): bool
{
return ! $this->isSubkey;
}

public function isSubkey(): bool
{
return $this->isSubkey;
}

public function hasSecretKey(): bool
{
return $this->hasSecretKey;
}

public function keyId(): SecretKeyId
{
return $this->keyId;
}

public function hasSignCapability(): bool
{
return str_contains($this->capabilities, 's');
}
}
36 changes: 36 additions & 0 deletions test/asset/dummy-gpg-key-no-secret.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----

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
=qlLW
-----END PGP PUBLIC KEY BLOCK-----
63 changes: 63 additions & 0 deletions test/asset/dummy-gpg-no-sign.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=cI7Z
-----END PGP PRIVATE KEY BLOCK-----
50 changes: 50 additions & 0 deletions test/asset/dummy-gpg-only-subkey.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----

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
=VmJU
-----END PGP PRIVATE KEY BLOCK-----
33 changes: 33 additions & 0 deletions test/unit/Gpg/ImportGpgKeyFromStringViaTemporaryFileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use Laminas\AutomaticReleases\Gpg\ImportGpgKeyFromStringViaTemporaryFile;
use Laminas\AutomaticReleases\Gpg\SecretKeyId;
use PHPUnit\Framework\TestCase;
use Psl\Exception\InvariantViolationException;
use Psl\Shell\Exception\FailedExecutionException;

use function Psl\File\read;

Expand All @@ -21,4 +23,35 @@ public function testWillImportValidGpgKey(): void
->__invoke(read(__DIR__ . '/../../asset/dummy-gpg-key.asc')),
);
}

public function testWillImportGpgKeyWithValidSubkey(): void
{
self::assertEquals(
SecretKeyId::fromBase16String('8CA5C026AE941316'),
(new ImportGpgKeyFromStringViaTemporaryFile())
->__invoke(read(__DIR__ . '/../../asset/dummy-gpg-only-subkey.asc')),
);
}

public function testWillFailOnNoSecretKey(): void
{
$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('Imported GPG key material does not contain secret key');
(new ImportGpgKeyFromStringViaTemporaryFile())
->__invoke(read(__DIR__ . '/../../asset/dummy-gpg-key-no-secret.asc'));
}

public function testWillFailOnMissingSignCapabilities(): void
{
$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('Imported GPG key material does not contain primary key or subkey with sign capabilities');
(new ImportGpgKeyFromStringViaTemporaryFile())
->__invoke(read(__DIR__ . '/../../asset/dummy-gpg-no-sign.asc'));
}

public function testWillFailOnInvalidGpgKey(): void
{
$this->expectException(FailedExecutionException::class);
(new ImportGpgKeyFromStringViaTemporaryFile())->__invoke('-----BEGIN PGP PRIVATE KEY BLOCK-----');
}
}
Loading

0 comments on commit 5db5062

Please sign in to comment.