Added support to lookup public keys online #89

This commit is contained in:
the-djmaze 2024-02-07 01:55:52 +01:00
parent bac6c2d332
commit 6c01db395d
4 changed files with 47 additions and 9 deletions

View file

@ -4,11 +4,16 @@ import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
import { AbstractViewPopup } from 'Knoin/AbstractViews'; import { AbstractViewPopup } from 'Knoin/AbstractViews';
import Remote from 'Remote/User/Fetch';
import { i18n } from 'Common/Translator';
export class OpenPgpImportPopupView extends AbstractViewPopup { export class OpenPgpImportPopupView extends AbstractViewPopup {
constructor() { constructor() {
super('OpenPgpImport'); super('OpenPgpImport');
addObservablesTo(this, { addObservablesTo(this, {
search: '',
key: '', key: '',
keyError: false, keyError: false,
keyErrorMessage: '', keyErrorMessage: '',
@ -25,6 +30,21 @@ export class OpenPgpImportPopupView extends AbstractViewPopup {
}); });
} }
searchPGP() {
this.key(i18n('SUGGESTIONS/SEARCHING_DESC'));
Remote.request('SearchPGPKey',
(iError, oData) => {
if (iError) {
this.key(oData.ErrorMessage);
} else {
this.key(oData.Result);
}
}, {
query: this.search()
}
);
}
submitForm() { submitForm() {
let keyTrimmed = this.key().trim(); let keyTrimmed = this.key().trim();

View file

@ -10,6 +10,14 @@ trait Pgp
* Also see trait Messages::DoMessagePgpVerify * Also see trait Messages::DoMessagePgpVerify
*/ */
public function DoSearchPGPKey() : array
{
$result = \SnappyMail\PGP\Keyservers::get(
$this->GetActionParam('query', '')
);
return $this->DefaultResponse($result ?: false);
}
/** /**
* @throws \MailSo\RuntimeException * @throws \MailSo\RuntimeException
*/ */

View file

@ -8,6 +8,10 @@
* https://datatracker.ietf.org/doc/html/rfc4387 * https://datatracker.ietf.org/doc/html/rfc4387
* https://datatracker.ietf.org/doc/html/rfc7929 * https://datatracker.ietf.org/doc/html/rfc7929
* https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service-13 * https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service-13
*
* GET https://keys.openpgp.org/pks/lookup?op=get&options=mr&search=security@snappymail.eu
* GET https://keys.openpgp.org/vks/v1/by-fingerprint/445D265124E6072671E64D0733F868A7E35E8277
* GET https://openpgpkey.example.org/.well-known/openpgpkey/example.org/hu/ihyath4noz8dsckzjbuyqnh4kbup6h4i?l=john.doe
*/ */
namespace SnappyMail\PGP; namespace SnappyMail\PGP;
@ -15,18 +19,16 @@ namespace SnappyMail\PGP;
abstract class Keyservers abstract class Keyservers
{ {
public static $hosts = [ public static $hosts = [
'https://keys.openpgp.org'
/* /*
'https://keys.openpgp.org',
'https://pgp.mit.edu', 'https://pgp.mit.edu',
'https://keyring.debian.org', 'https://keyring.debian.org',
'https://attester.flowcrypt.com', 'https://attester.flowcrypt.com',
'https://zimmermann.mayfirst.org', 'https://zimmermann.mayfirst.org',
'https://pool.sks-keyservers.net', 'https://pool.sks-keyservers.net',
'https://keys.mailvelope.com', 'https://keys.mailvelope.com',
*/
'https://keyserver.ubuntu.com', 'https://keyserver.ubuntu.com',
'https://keys.fedoraproject.org', */
'https://keys.openpgp.org'
]; ];
private static function fetch(string $host, string $op, string $search, bool $fingerprint = false, bool $exact = false) : ?\SnappyMail\HTTP\Response private static function fetch(string $host, string $op, string $search, bool $fingerprint = false, bool $exact = false) : ?\SnappyMail\HTTP\Response
@ -36,7 +38,9 @@ abstract class Keyservers
$search = \urlencode($search); $search = \urlencode($search);
$fingerprint = $fingerprint ? '&fingerprint=on' : ''; $fingerprint = $fingerprint ? '&fingerprint=on' : '';
$exact = $exact ? '&exact=on' : ''; $exact = $exact ? '&exact=on' : '';
return static::HTTP()->doRequest('GET', "{$host}/pks/lookup?op={$op}&options=mr{$fingerprint}&search={$search}"); $url = "{$host}/pks/lookup?op={$op}&options=mr{$fingerprint}&search={$search}";
\SnappyMail\Log::debug('PGP', $url);
return static::HTTP()->doRequest('GET', $url);
} }
private static $HTTP; private static $HTTP;
@ -56,11 +60,12 @@ abstract class Keyservers
*/ */
public static function get(string $keyId) : string public static function get(string $keyId) : string
{ {
/*
// add the 0x prefix if absent // add the 0x prefix if absent
if ('0x' !== \substr($keyId, 0, 2)) { if ('0x' !== \substr($keyId, 0, 2)) {
$keyId = '0x' . $keyId; $keyId = '0x' . $keyId;
} }
*/
foreach (static::$hosts as $host) { foreach (static::$hosts as $host) {
$oResponse = static::fetch($host, 'get', $keyId); $oResponse = static::fetch($host, 'get', $keyId);
if (!$oResponse) { if (!$oResponse) {
@ -68,14 +73,14 @@ abstract class Keyservers
continue; continue;
} }
if (200 !== $oResponse->status) { if (200 !== $oResponse->status) {
\SnappyMail\Log::info('PGP', "{$oResponse->status} for key {$keyId} on {$host}"); \SnappyMail\Log::debug('PGP', "{$oResponse->status} for key {$keyId} on {$host}");
continue; continue;
} }
return $oResponse->body; return $oResponse->body;
} }
throw new \Exception('Could not obtain public key from the keyserver.'); throw new \Exception('Could not obtain public key from the keyservers.');
} }
/** /**
@ -97,7 +102,7 @@ abstract class Keyservers
continue; continue;
} }
if (200 !== $oResponse->status) { if (200 !== $oResponse->status) {
\SnappyMail\Log::info('PGP', "{$oResponse->status} for search `{$search}` on {$host}"); \SnappyMail\Log::debug('PGP', "{$oResponse->status} for search `{$search}` on {$host}");
continue; continue;
} }

View file

@ -4,6 +4,11 @@
</header> </header>
<form id="openpgp-import" class="modal-body" autocomplete="off" data-bind="submit: submitForm"> <form id="openpgp-import" class="modal-body" autocomplete="off" data-bind="submit: submitForm">
<div class="alert" data-bind="visible: keyError() && keyErrorMessage(), text: keyErrorMessage"></div> <div class="alert" data-bind="visible: keyError() && keyErrorMessage(), text: keyErrorMessage"></div>
<div class="control-group">
<!-- could be email or fingerprint -->
<input type="text" data-bind="textInput: search"/>
<button class="btn" type="button" data-bind="enable: search, click: searchPGP" data-i18n="GLOBAL/SEARCH"></button>
</div>
<div class="control-group" data-bind="css: {'error': keyError}"> <div class="control-group" data-bind="css: {'error': keyError}">
<textarea class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: key" required=""></textarea> <textarea class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: key" required=""></textarea>
</div> </div>