web-base/Core/Objects/DatabaseEntity/GpgKey.class.php

155 lines
4.9 KiB
PHP
Raw Normal View History

2022-02-20 16:53:26 +01:00
<?php
2022-11-18 18:06:46 +01:00
namespace Core\Objects\DatabaseEntity;
2022-02-20 16:53:26 +01:00
2022-11-18 18:06:46 +01:00
use Core\Driver\SQL\Expression\CurrentTimeStamp;
2022-11-19 01:15:34 +01:00
use Core\Driver\SQL\SQL;
2022-11-18 18:06:46 +01:00
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
2022-11-20 17:13:53 +01:00
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
2022-06-20 19:52:31 +02:00
class GpgKey extends DatabaseEntity {
2022-02-20 16:53:26 +01:00
const GPG2 = "/usr/bin/gpg2";
private bool $confirmed;
2022-06-20 19:52:31 +02:00
#[MaxLength(64)] private string $fingerprint;
#[MaxLength(64)] private string $algorithm;
2022-02-20 16:53:26 +01:00
private \DateTime $expires;
2022-06-20 19:52:31 +02:00
#[DefaultValue(CurrentTimeStamp::class)] private \DateTime $added;
2022-02-20 16:53:26 +01:00
2022-11-19 01:15:34 +01:00
public function __construct(string $fingerprint, string $algorithm, \DateTime $expires) {
parent::__construct();
$this->confirmed = false;
2022-02-20 16:53:26 +01:00
$this->fingerprint = $fingerprint;
$this->algorithm = $algorithm;
2022-11-19 01:15:34 +01:00
$this->expires = $expires;
$this->added = new \DateTime();
2022-02-20 16:53:26 +01:00
}
2023-03-05 15:30:06 +01:00
public function _encrypt(string $body): array {
return self::encrypt($body, $this->fingerprint);
}
2022-02-20 16:53:26 +01:00
public static function encrypt(string $body, string $gpgFingerprint): array {
$gpgFingerprint = escapeshellarg($gpgFingerprint);
$cmd = self::GPG2 . " --encrypt --output - --recipient $gpgFingerprint --trust-model always --batch --armor";
list($out, $err) = self::proc_exec($cmd, $body, true);
if ($out === null) {
2022-06-20 19:52:31 +02:00
return createError("Error while communicating with GPG agent");
2022-02-20 16:53:26 +01:00
} else if ($err) {
2022-06-20 19:52:31 +02:00
return createError($err);
2022-02-20 16:53:26 +01:00
} else {
return ["success" => true, "data" => $out];
}
}
2023-03-05 15:30:06 +01:00
public function _sign(string $body): array {
return self::sign($body, $this->fingerprint);
}
public static function sign(string $body, string $gpgFingerprint): array {
$gpgFingerprint = escapeshellarg($gpgFingerprint);
$cmd = self::GPG2 . " --clearsign --output - --local-user $gpgFingerprint --batch --armor";
list($out, $err) = self::proc_exec($cmd, $body, true);
if ($out === null) {
return createError("Error while communicating with GPG agent");
} else if ($err) {
return createError($err);
} else {
return ["success" => true, "data" => $out];
}
}
2022-02-20 16:53:26 +01:00
private static function proc_exec(string $cmd, ?string $stdin = null, bool $raw = false): ?array {
$descriptorSpec = array(0 => ["pipe", "r"], 1 => ["pipe", "w"], 2 => ["pipe", "w"]);
2022-06-20 19:52:31 +02:00
$process = proc_open($cmd, $descriptorSpec, $pipes);
2022-02-20 16:53:26 +01:00
if (!is_resource($process)) {
return null;
}
if ($stdin) {
fwrite($pipes[0], $stdin);
fclose($pipes[0]);
}
$out = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return [($raw ? $out : trim($out)), $err];
}
public static function getKeyInfo(string $key): array {
list($out, $err) = self::proc_exec(self::GPG2 . " --show-key", $key);
if ($out === null) {
2022-06-20 19:52:31 +02:00
return createError("Error while communicating with GPG agent");
2022-02-20 16:53:26 +01:00
}
if ($err) {
2022-06-20 19:52:31 +02:00
return createError($err);
2022-02-20 16:53:26 +01:00
}
$lines = explode("\n", $out);
if (count($lines) > 4) {
2022-06-20 19:52:31 +02:00
return createError("It seems like you have uploaded more than one GPG-Key");
2022-02-20 16:53:26 +01:00
} else if (count($lines) !== 4 || !preg_match("/(\S+)\s+(\w+)\s+.*\[expires: ([0-9-]+)]/", $lines[0], $matches)) {
2022-06-20 19:52:31 +02:00
return createError("Error parsing GPG output");
2022-02-20 16:53:26 +01:00
}
$keyType = $matches[1];
2022-06-20 19:52:31 +02:00
$keyAlg = $matches[2];
2022-02-20 16:53:26 +01:00
$expires = \DateTime::createFromFormat("Y-m-d", $matches[3]);
$fingerprint = trim($lines[1]);
$keyData = ["type" => $keyType, "algorithm" => $keyAlg, "expires" => $expires, "fingerprint" => $fingerprint];
return ["success" => true, "data" => $keyData];
}
public static function importKey(string $key): array {
list($out, $err) = self::proc_exec(self::GPG2 . " --import", $key);
if ($out === null) {
2022-06-20 19:52:31 +02:00
return createError("Error while communicating with GPG agent");
2022-02-20 16:53:26 +01:00
}
if (preg_match("/gpg:\s+Total number processed:\s+(\d+)/", $err, $matches) && intval($matches[1]) > 0) {
if ((preg_match("/.*\s+unchanged:\s+(\d+)/", $err, $matches) && intval($matches[1]) > 0) ||
2022-06-20 19:52:31 +02:00
(preg_match("/.*\s+imported:\s+(\d+)/", $err, $matches) && intval($matches[1]) > 0)) {
2022-02-20 16:53:26 +01:00
return ["success" => true];
}
}
2022-06-20 19:52:31 +02:00
return createError($err);
2022-02-20 16:53:26 +01:00
}
2023-03-05 15:30:06 +01:00
public function _export(bool $armored = true): array {
return self::export($this->fingerprint, $armored);
}
public static function export(string $gpgFingerprint, bool $armored): array {
2022-02-20 16:53:26 +01:00
$cmd = self::GPG2 . " --export ";
if ($armored) {
$cmd .= "--armor ";
}
$cmd .= escapeshellarg($gpgFingerprint);
list($out, $err) = self::proc_exec($cmd);
if ($err) {
2022-06-20 19:52:31 +02:00
return createError($err);
2022-02-20 16:53:26 +01:00
}
return ["success" => true, "data" => $out];
}
public function isConfirmed(): bool {
return $this->confirmed;
}
public function getFingerprint(): string {
return $this->fingerprint;
}
2022-11-19 01:15:34 +01:00
public function confirm(SQL $sql): bool {
$this->confirmed = true;
2022-11-29 14:17:11 +01:00
return $this->save($sql, ["confirmed"]);
2022-11-19 01:15:34 +01:00
}
2022-02-20 16:53:26 +01:00
}