Added Base32 for handling GnuPG WKD key finder

This commit is contained in:
the-djmaze 2026-10-28 02:51:22 +01:00
parent 6d0b2121b8
commit b792fd461d
3 changed files with 102 additions and 4 deletions

View file

@ -0,0 +1,84 @@
<?php
namespace SnappyMail;
class Base32
{
private string $chars;
function __construct(string $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
{
$this->chars = $chars;
}
public function random(int $length): string
{
$secret = '';
while (0 < $length--) {
$secret .= $this->chars[\random_int(0,31)];
}
return $secret;
}
public function encode(string $data, bool $padding = false, int $chunklen = 0, string $end = "\r\n"): string
{
$sl = \is_string($data) ? \strlen($data) : 0;
$base32 = '';
$remainder = 0;
$remainderSize = 0;
for ($i = 0; $i < $sl; ++$i) {
$remainder = ($remainder << 8) | \ord($data[$i]);
$remainderSize += 8;
while ($remainderSize > 4) {
$remainderSize -= 5;
$base32 .= $this->chars[($remainder & (31 << $remainderSize)) >> $remainderSize];
}
}
if ($remainderSize > 0) {
// remainderSize < 5:
$base32 .= $this->chars[($remainder << (5 - $remainderSize)) & 31];
}
if ($padding) {
$base32 .= \str_repeat('=', (8 - \ceil(($sl % 5) * 8 / 5)) % 8);
}
return 0 < $chunklen
? \chunk_split($base32, $chunklen, $end)
: $base32;
}
public function decode(string $data, bool $strict = false): ?string
{
$data = \strtoupper(\rtrim($data, "=\x20\t\n\r\0\x0B"));
$dataSize = \strlen($data);
$buf = 0;
$bufSize = 0;
$res = '';
$charMap = \array_flip(\str_split($this->chars));
for ($i = 0; $i < $dataSize; ++$i) {
$c = $data[$i];
if (isset($charMap[$c])) {
$buf = ($buf << 5) | $charMap[$c];
$bufSize += 5;
if ($bufSize > 7) {
$bufSize -= 8;
$res .= \chr(($buf & (0xff << $bufSize)) >> $bufSize);
}
} else if ($strict && false === \strpos(" \r\n\t", $c)) {
\trigger_error('Base32 string contains unexpected char #'.\ord($c)." at offset {$i}");
return null;
}
}
return $res;
}
public function verify(string $data): bool
{
return (bool) \preg_match('/^['.\preg_quote($this->chars).' \\r\\n\\t]+=*$/D', $data);
}
}

View file

@ -398,6 +398,11 @@ class PGP extends Base implements \SnappyMail\PGP\PGPInterface
}
}
/**
* Import remote key from WKD
gpg --auto-key-locate clear,wkd --locate-keys releases@snappymail.eu
*/
public function deleteKey(string $keyId, bool $private): bool
{
$key = $this->keyInfo($keyId, $private);

View file

@ -28,6 +28,7 @@ abstract class Keyservers
'https://pool.sks-keyservers.net',
'https://keys.mailvelope.com',
'https://keyserver.ubuntu.com',
'hkps://openpgpkeyserver.com',
*/
];
@ -196,10 +197,18 @@ abstract class Keyservers
*/
}
/** https://wiki.gnupg.org/WKD */
public static function wkd(string $host, string $keyid)
/**
* https://wiki.gnupg.org/WKD
* gpg --with-wkd-hash --fingerprint releases@snappymail.eu
* gpg --no-armor --export releases@snappymail.eu > i4cdqgcarfjdjnba6y4jnf498asg8c6p
*/
public static function wkd(string $host, string $emailAddress)
{
/*
$localPart = \explode('@', $emailAddress)[0];
$b32 = new \SnappyMail\Base32('ybndrfg8ejkmcpqxot1uwisza345h769');
$keyid = $b32->encode(\sha1($localPart, true));
"https://{$host}/.well-known/openpgpkey/hu/{$keyid}";
/** https://wiki.gnupg.org/WKD
DNS:
@ -208,8 +217,8 @@ abstract class Keyservers
https://openpgpkey.example.com/.well-known/openpgpkey/example.com/hu/
else https://example.com/.well-known/openpgpkey/hu/
An example: https://example.com/.well-known/openpgpkey/hu/it5sewh54rxz33fwmr8u6dy4bbz8itz4
is the direct method URL for "bernhard.reiter@example.com"
An example: https://snappymail.eu/.well-known/openpgpkey/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p
is the direct method URL for "releases@snappymail.eu"
*/
}