summaryrefslogtreecommitdiffstats
path: root/comm/mail/extensions/openpgp/content/modules/keyserver.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/extensions/openpgp/content/modules/keyserver.jsm')
-rw-r--r--comm/mail/extensions/openpgp/content/modules/keyserver.jsm1549
1 files changed, 1549 insertions, 0 deletions
diff --git a/comm/mail/extensions/openpgp/content/modules/keyserver.jsm b/comm/mail/extensions/openpgp/content/modules/keyserver.jsm
new file mode 100644
index 0000000000..a2c66ade63
--- /dev/null
+++ b/comm/mail/extensions/openpgp/content/modules/keyserver.jsm
@@ -0,0 +1,1549 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+const EXPORTED_SYMBOLS = ["EnigmailKeyServer"];
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const lazy = {};
+
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ EnigmailConstants: "chrome://openpgp/content/modules/constants.jsm",
+ EnigmailCryptoAPI: "chrome://openpgp/content/modules/cryptoAPI.jsm",
+ EnigmailData: "chrome://openpgp/content/modules/data.jsm",
+ EnigmailFuncs: "chrome://openpgp/content/modules/funcs.jsm",
+ EnigmailKeyRing: "chrome://openpgp/content/modules/keyRing.jsm",
+ EnigmailLog: "chrome://openpgp/content/modules/log.jsm",
+ FeedUtils: "resource:///modules/FeedUtils.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(lazy, "l10n", () => {
+ return new Localization(["messenger/openpgp/openpgp.ftl"], true);
+});
+
+const ENIG_DEFAULT_HKP_PORT = "11371";
+const ENIG_DEFAULT_HKPS_PORT = "443";
+const ENIG_DEFAULT_LDAP_PORT = "389";
+
+/**
+ KeySrvListener API
+ Object implementing:
+ - onProgress: function(percentComplete) [only implemented for download()]
+ - onCancel: function() - the body will be set by the callee
+*/
+
+function createError(errId) {
+ let msg = "";
+
+ switch (errId) {
+ case lazy.EnigmailConstants.KEYSERVER_ERR_ABORTED:
+ msg = lazy.l10n.formatValueSync("keyserver-error-aborted");
+ break;
+ case lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR:
+ msg = lazy.l10n.formatValueSync("keyserver-error-server-error");
+ break;
+ case lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_UNAVAILABLE:
+ msg = lazy.l10n.formatValueSync("keyserver-error-unavailable");
+ break;
+ case lazy.EnigmailConstants.KEYSERVER_ERR_SECURITY_ERROR:
+ msg = lazy.l10n.formatValueSync("keyserver-error-security-error");
+ break;
+ case lazy.EnigmailConstants.KEYSERVER_ERR_CERTIFICATE_ERROR:
+ msg = lazy.l10n.formatValueSync("keyserver-error-certificate-error");
+ break;
+ case lazy.EnigmailConstants.KEYSERVER_ERR_IMPORT_ERROR:
+ msg = lazy.l10n.formatValueSync("keyserver-error-import-error");
+ break;
+ case lazy.EnigmailConstants.KEYSERVER_ERR_UNKNOWN:
+ msg = lazy.l10n.formatValueSync("keyserver-error-unknown");
+ break;
+ }
+
+ return {
+ result: errId,
+ errorDetails: msg,
+ };
+}
+
+/**
+ * parse a keyserver specification and return host, protocol and port
+ *
+ * @param keyserver: String - name of keyserver with optional protocol and port.
+ * E.g. keys.gnupg.net, hkps://keys.gnupg.net:443
+ *
+ * @returns Object: {port, host, protocol} (all Strings)
+ */
+function parseKeyserverUrl(keyserver) {
+ if (keyserver.length > 1024) {
+ // insane length of keyserver is forbidden
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ }
+
+ keyserver = keyserver.toLowerCase().trim();
+ let protocol = "";
+ if (keyserver.search(/^[a-zA-Z0-9_.-]+:\/\//) === 0) {
+ protocol = keyserver.replace(/^([a-zA-Z0-9_.-]+)(:\/\/.*)/, "$1");
+ keyserver = keyserver.replace(/^[a-zA-Z0-9_.-]+:\/\//, "");
+ } else {
+ protocol = "hkp";
+ }
+
+ let port = "";
+ switch (protocol) {
+ case "hkp":
+ port = ENIG_DEFAULT_HKP_PORT;
+ break;
+ case "https":
+ case "hkps":
+ port = ENIG_DEFAULT_HKPS_PORT;
+ break;
+ case "ldap":
+ port = ENIG_DEFAULT_LDAP_PORT;
+ break;
+ }
+
+ let m = keyserver.match(/^(.+)(:)(\d+)$/);
+ if (m && m.length == 4) {
+ keyserver = m[1];
+ port = m[3];
+ }
+
+ if (keyserver.search(/^(keys\.mailvelope\.com|api\.protonmail\.ch)$/) === 0) {
+ protocol = "hkps";
+ port = ENIG_DEFAULT_HKPS_PORT;
+ }
+ if (keyserver.search(/^(keybase\.io)$/) === 0) {
+ protocol = "keybase";
+ port = ENIG_DEFAULT_HKPS_PORT;
+ }
+
+ return {
+ protocol,
+ host: keyserver,
+ port,
+ };
+}
+
+/**
+ Object to handle HKP/HKPS requests via builtin XMLHttpRequest()
+ */
+const accessHkpInternal = {
+ /**
+ * Create the payload of hkp requests (upload only)
+ *
+ */
+ async buildHkpPayload(actionFlag, searchTerms) {
+ switch (actionFlag) {
+ case lazy.EnigmailConstants.UPLOAD_KEY:
+ let exitCodeObj = {};
+ let keyData = await lazy.EnigmailKeyRing.extractPublicKeys(
+ ["0x" + searchTerms], // TODO: confirm input is ID or fingerprint
+ null,
+ null,
+ null,
+ exitCodeObj,
+ {}
+ );
+ if (exitCodeObj.value !== 0 || keyData.length === 0) {
+ return null;
+ }
+ return 'keytext="' + encodeURIComponent(keyData) + '"';
+
+ case lazy.EnigmailConstants.DOWNLOAD_KEY:
+ case lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT:
+ case lazy.EnigmailConstants.SEARCH_KEY:
+ return "";
+ }
+
+ // other actions are not yet implemented
+ return null;
+ },
+
+ /**
+ * return the URL and the HTTP access method for a given action
+ */
+ createRequestUrl(keyserver, actionFlag, searchTerm) {
+ let keySrv = parseKeyserverUrl(keyserver);
+
+ let method = "GET";
+ let protocol;
+
+ switch (keySrv.protocol) {
+ case "hkp":
+ protocol = "http";
+ break;
+ case "ldap":
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ default:
+ // equals to hkps
+ protocol = "https";
+ }
+
+ let url = protocol + "://" + keySrv.host + ":" + keySrv.port;
+
+ if (actionFlag === lazy.EnigmailConstants.UPLOAD_KEY) {
+ url += "/pks/add";
+ method = "POST";
+ } else if (
+ actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY ||
+ actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT
+ ) {
+ if (searchTerm.indexOf("0x") !== 0) {
+ searchTerm = "0x" + searchTerm;
+ }
+ url += "/pks/lookup?search=" + searchTerm + "&op=get&options=mr";
+ } else if (actionFlag === lazy.EnigmailConstants.SEARCH_KEY) {
+ url +=
+ "/pks/lookup?search=" +
+ escape(searchTerm) +
+ "&fingerprint=on&op=index&options=mr&exact=on";
+ }
+
+ return {
+ url,
+ host: keySrv.host,
+ method,
+ };
+ },
+
+ /**
+ * Upload, search or download keys from a keyserver
+ *
+ * @param actionFlag: Number - Keyserver Action Flags: from EnigmailConstants
+ * @param keyId: String - space-separated list of search terms or key IDs
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Number (Status-ID)>
+ */
+ async accessKeyServer(actionFlag, keyserver, keyId, listener) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessHkpInternal.accessKeyServer(${keyserver})\n`
+ );
+ if (!keyserver) {
+ throw new Error("accessKeyServer requires explicit keyserver parameter");
+ }
+
+ let payLoad = await this.buildHkpPayload(actionFlag, keyId);
+
+ return new Promise((resolve, reject) => {
+ let xmlReq = null;
+ if (listener && typeof listener === "object") {
+ listener.onCancel = function () {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessHkpInternal.accessKeyServer - onCancel() called\n`
+ );
+ if (xmlReq) {
+ xmlReq.abort();
+ }
+ reject(createError(lazy.EnigmailConstants.KEYSERVER_ERR_ABORTED));
+ };
+ }
+ if (actionFlag === lazy.EnigmailConstants.REFRESH_KEY) {
+ // we don't (need to) distinguish between refresh and download for our internal protocol
+ actionFlag = lazy.EnigmailConstants.DOWNLOAD_KEY;
+ }
+
+ if (payLoad === null) {
+ reject(createError(lazy.EnigmailConstants.KEYSERVER_ERR_UNKNOWN));
+ return;
+ }
+
+ xmlReq = new XMLHttpRequest();
+
+ xmlReq.onload = function () {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessHkpInternal: onload(): status=" +
+ xmlReq.status +
+ "\n"
+ );
+ switch (actionFlag) {
+ case lazy.EnigmailConstants.UPLOAD_KEY:
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessHkpInternal: onload: " +
+ xmlReq.responseText +
+ "\n"
+ );
+ if (xmlReq.status >= 400) {
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ resolve(0);
+ }
+ return;
+
+ case lazy.EnigmailConstants.SEARCH_KEY:
+ if (xmlReq.status === 404) {
+ // key not found
+ resolve("");
+ } else if (xmlReq.status >= 400) {
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ resolve(xmlReq.responseText);
+ }
+ return;
+
+ case lazy.EnigmailConstants.DOWNLOAD_KEY:
+ case lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT:
+ if (xmlReq.status >= 400 && xmlReq.status < 500) {
+ // key not found
+ resolve(1);
+ } else if (xmlReq.status >= 500) {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessHkpInternal: onload: " +
+ xmlReq.responseText +
+ "\n"
+ );
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ let errorMsgObj = {},
+ importedKeysObj = {};
+
+ if (actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY) {
+ let importMinimal = false;
+ let r = lazy.EnigmailKeyRing.importKey(
+ null,
+ false,
+ xmlReq.responseText,
+ false,
+ "",
+ errorMsgObj,
+ importedKeysObj,
+ importMinimal
+ );
+ if (r === 0) {
+ resolve(importedKeysObj.value);
+ } else {
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_IMPORT_ERROR
+ )
+ );
+ }
+ } else {
+ // DOWNLOAD_KEY_NO_IMPORT
+ resolve(xmlReq.responseText);
+ }
+ }
+ return;
+ }
+ resolve(-1);
+ };
+
+ xmlReq.onerror = function (e) {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessHkpInternal.accessKeyServer: onerror: " +
+ e +
+ "\n"
+ );
+ let err = lazy.FeedUtils.createTCPErrorFromFailedXHR(e.target);
+ switch (err.type) {
+ case "SecurityCertificate":
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_CERTIFICATE_ERROR
+ )
+ );
+ break;
+ case "SecurityProtocol":
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SECURITY_ERROR)
+ );
+ break;
+ case "Network":
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_UNAVAILABLE
+ )
+ );
+ break;
+ }
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_UNAVAILABLE)
+ );
+ };
+
+ xmlReq.onloadend = function () {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessHkpInternal.accessKeyServer: loadEnd\n"
+ );
+ };
+
+ let { url, method } = this.createRequestUrl(keyserver, actionFlag, keyId);
+
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessHkpInternal.accessKeyServer: requesting ${url}\n`
+ );
+ xmlReq.open(method, url);
+ xmlReq.send(payLoad);
+ });
+ },
+
+ /**
+ * Download keys from a keyserver
+ *
+ * @param keyIDs: String - space-separated list of search terms or key IDs
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<...>
+ */
+ async download(autoImport, keyIDs, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessHkpInternal.download(${keyIDs})\n`
+ );
+ let keyIdArr = keyIDs.split(/ +/);
+ let retObj = {
+ result: 0,
+ errorDetails: "",
+ keyList: [],
+ };
+
+ for (let i = 0; i < keyIdArr.length; i++) {
+ try {
+ let r = await this.accessKeyServer(
+ autoImport
+ ? lazy.EnigmailConstants.DOWNLOAD_KEY
+ : lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT,
+ keyserver,
+ keyIdArr[i],
+ listener
+ );
+ if (autoImport) {
+ if (Array.isArray(r)) {
+ retObj.keyList = retObj.keyList.concat(r);
+ }
+ } else if (typeof r == "string") {
+ retObj.keyData = r;
+ } else {
+ retObj.result = r;
+ }
+ } catch (ex) {
+ retObj.result = ex.result;
+ retObj.errorDetails = ex.errorDetails;
+ throw retObj;
+ }
+
+ if (listener && "onProgress" in listener) {
+ listener.onProgress(((i + 1) / keyIdArr.length) * 100);
+ }
+ }
+
+ return retObj;
+ },
+
+ refresh(keyServer, listener = null) {
+ let keyList = lazy.EnigmailKeyRing.getAllKeys()
+ .keyList.map(keyObj => {
+ return "0x" + keyObj.fpr;
+ })
+ .join(" ");
+
+ return this.download(true, keyList, keyServer, listener);
+ },
+
+ /**
+ * Upload keys to a keyserver
+ *
+ * @param keyIDs: String - space-separated list of search terms or key IDs
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return {boolean} - Returns true if the key was sent successfully
+ */
+ async upload(keyIDs, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessHkpInternal.upload(${keyIDs})\n`
+ );
+ let keyIdArr = keyIDs.split(/ +/);
+ let rv = false;
+
+ for (let i = 0; i < keyIdArr.length; i++) {
+ try {
+ let r = await this.accessKeyServer(
+ lazy.EnigmailConstants.UPLOAD_KEY,
+ keyserver,
+ keyIdArr[i],
+ listener
+ );
+ if (r === 0) {
+ rv = true;
+ } else {
+ rv = false;
+ break;
+ }
+ } catch (ex) {
+ console.log(ex.errorDetails);
+ rv = false;
+ break;
+ }
+
+ if (listener && "onProgress" in listener) {
+ listener.onProgress(((i + 1) / keyIdArr.length) * 100);
+ }
+ }
+
+ return rv;
+ },
+
+ /**
+ * Search for keys on a keyserver
+ *
+ * @param searchTerm: String - search term
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Object>
+ * - result: Number
+ * - pubKeys: Array of Object:
+ * PubKeys: Object with:
+ * - keyId: String
+ * - keyLen: String
+ * - keyType: String
+ * - created: String (YYYY-MM-DD)
+ * - status: String: one of ''=valid, r=revoked, e=expired
+ * - uid: Array of Strings with UIDs
+ */
+ async searchKeyserver(searchTerm, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessHkpInternal.search(${searchTerm})\n`
+ );
+ let retObj = {
+ result: 0,
+ errorDetails: "",
+ pubKeys: [],
+ };
+ let key = null;
+
+ let searchArr = searchTerm.split(/ +/);
+
+ for (let k in searchArr) {
+ let r = await this.accessKeyServer(
+ lazy.EnigmailConstants.SEARCH_KEY,
+ keyserver,
+ searchArr[k],
+ listener
+ );
+
+ let lines = r.split(/\r?\n/);
+
+ for (var i = 0; i < lines.length; i++) {
+ let line = lines[i].split(/:/).map(unescape);
+ if (line.length <= 1) {
+ continue;
+ }
+
+ switch (line[0]) {
+ case "info":
+ if (line[1] !== "1") {
+ // protocol version not supported
+ retObj.result = 7;
+ retObj.errorDetails = await lazy.l10n.formatValue(
+ "keyserver-error-unsupported"
+ );
+ retObj.pubKeys = [];
+ return retObj;
+ }
+ break;
+ case "pub":
+ if (line.length >= 6) {
+ if (key) {
+ retObj.pubKeys.push(key);
+ key = null;
+ }
+ let dat = new Date(line[4] * 1000);
+ let month = String(dat.getMonth() + 101).substr(1);
+ let day = String(dat.getDate() + 100).substr(1);
+ key = {
+ keyId: line[1],
+ keyLen: line[3],
+ keyType: line[2],
+ created: dat.getFullYear() + "-" + month + "-" + day,
+ uid: [],
+ status: line[6],
+ };
+ }
+ break;
+ case "uid":
+ key.uid.push(
+ lazy.EnigmailData.convertToUnicode(line[1].trim(), "utf-8")
+ );
+ }
+ }
+
+ if (key) {
+ retObj.pubKeys.push(key);
+ }
+ }
+
+ return retObj;
+ },
+};
+
+/**
+ Object to handle KeyBase requests (search & download only)
+ */
+const accessKeyBase = {
+ /**
+ * return the URL and the HTTP access method for a given action
+ */
+ createRequestUrl(actionFlag, searchTerm) {
+ let url = "https://keybase.io/_/api/1.0/user/";
+
+ if (actionFlag === lazy.EnigmailConstants.UPLOAD_KEY) {
+ // not supported
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ } else if (
+ actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY ||
+ actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT
+ ) {
+ if (searchTerm.indexOf("0x") === 0) {
+ searchTerm = searchTerm.substr(0, 40);
+ }
+ url +=
+ "lookup.json?key_fingerprint=" +
+ escape(searchTerm) +
+ "&fields=public_keys";
+ } else if (actionFlag === lazy.EnigmailConstants.SEARCH_KEY) {
+ url += "autocomplete.json?q=" + escape(searchTerm);
+ }
+
+ return {
+ url,
+ method: "GET",
+ };
+ },
+
+ /**
+ * Upload, search or download keys from a keyserver
+ *
+ * @param actionFlag: Number - Keyserver Action Flags: from EnigmailConstants
+ * @param keyId: String - space-separated list of search terms or key IDs
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Number (Status-ID)>
+ */
+ async accessKeyServer(actionFlag, keyId, listener) {
+ lazy.EnigmailLog.DEBUG(`keyserver.jsm: accessKeyBase: accessKeyServer()\n`);
+
+ return new Promise((resolve, reject) => {
+ let xmlReq = null;
+ if (listener && typeof listener === "object") {
+ listener.onCancel = function () {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessKeyBase: accessKeyServer - onCancel() called\n`
+ );
+ if (xmlReq) {
+ xmlReq.abort();
+ }
+ reject(createError(lazy.EnigmailConstants.KEYSERVER_ERR_ABORTED));
+ };
+ }
+ if (actionFlag === lazy.EnigmailConstants.REFRESH_KEY) {
+ // we don't (need to) distinguish between refresh and download for our internal protocol
+ actionFlag = lazy.EnigmailConstants.DOWNLOAD_KEY;
+ }
+
+ xmlReq = new XMLHttpRequest();
+
+ xmlReq.onload = function () {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: onload(): status=" + xmlReq.status + "\n"
+ );
+ switch (actionFlag) {
+ case lazy.EnigmailConstants.SEARCH_KEY:
+ if (xmlReq.status >= 400) {
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ resolve(xmlReq.responseText);
+ }
+ return;
+
+ case lazy.EnigmailConstants.DOWNLOAD_KEY:
+ case lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT:
+ if (xmlReq.status >= 400 && xmlReq.status < 500) {
+ // key not found
+ resolve(1);
+ } else if (xmlReq.status >= 500) {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: onload: " + xmlReq.responseText + "\n"
+ );
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ try {
+ let resp = JSON.parse(xmlReq.responseText);
+ if (resp.status.code === 0) {
+ for (let hit in resp.them) {
+ lazy.EnigmailLog.DEBUG(
+ JSON.stringify(resp.them[hit].public_keys.primary) + "\n"
+ );
+
+ if (resp.them[hit] !== null) {
+ let errorMsgObj = {},
+ importedKeysObj = {};
+
+ if (actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY) {
+ let r = lazy.EnigmailKeyRing.importKey(
+ null,
+ false,
+ resp.them[hit].public_keys.primary.bundle,
+ false,
+ "",
+ errorMsgObj,
+ importedKeysObj
+ );
+ if (r === 0) {
+ resolve(importedKeysObj.value);
+ } else {
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_IMPORT_ERROR
+ )
+ );
+ }
+ } else {
+ // DOWNLOAD_KEY_NO_IMPORT
+ resolve(resp.them[hit].public_keys.primary.bundle);
+ }
+ }
+ }
+ }
+ } catch (ex) {
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_UNKNOWN)
+ );
+ }
+ }
+ return;
+ }
+ resolve(-1);
+ };
+
+ xmlReq.onerror = function (e) {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessKeyBase: onerror: " + e + "\n"
+ );
+ let err = lazy.FeedUtils.createTCPErrorFromFailedXHR(e.target);
+ switch (err.type) {
+ case "SecurityCertificate":
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_CERTIFICATE_ERROR
+ )
+ );
+ break;
+ case "SecurityProtocol":
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SECURITY_ERROR)
+ );
+ break;
+ case "Network":
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_UNAVAILABLE
+ )
+ );
+ break;
+ }
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_UNAVAILABLE)
+ );
+ };
+
+ xmlReq.onloadend = function () {
+ lazy.EnigmailLog.DEBUG("keyserver.jsm: accessKeyBase: loadEnd\n");
+ };
+
+ let { url, method } = this.createRequestUrl(actionFlag, keyId);
+
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessKeyBase: requesting ${url}\n`
+ );
+ xmlReq.open(method, url);
+ xmlReq.send("");
+ });
+ },
+
+ /**
+ * Download keys from a KeyBase
+ *
+ * @param keyIDs: String - space-separated list of search terms or key IDs
+ * @param keyserver: (not used for keybase)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<...>
+ */
+ async download(autoImport, keyIDs, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(`keyserver.jsm: accessKeyBase: download()\n`);
+ let keyIdArr = keyIDs.split(/ +/);
+ let retObj = {
+ result: 0,
+ errorDetails: "",
+ keyList: [],
+ };
+
+ for (let i = 0; i < keyIdArr.length; i++) {
+ try {
+ let r = await this.accessKeyServer(
+ autoImport
+ ? lazy.EnigmailConstants.DOWNLOAD_KEY
+ : lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT,
+ keyIdArr[i],
+ listener
+ );
+ if (r.length > 0) {
+ retObj.keyList = retObj.keyList.concat(r);
+ }
+ } catch (ex) {
+ retObj.result = ex.result;
+ retObj.errorDetails = ex.result;
+ throw retObj;
+ }
+
+ if (listener && "onProgress" in listener) {
+ listener.onProgress(i / keyIdArr.length);
+ }
+ }
+
+ return retObj;
+ },
+
+ /**
+ * Search for keys on a keyserver
+ *
+ * @param searchTerm: String - search term
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Object>
+ * - result: Number
+ * - pubKeys: Array of Object:
+ * PubKeys: Object with:
+ * - keyId: String
+ * - keyLen: String
+ * - keyType: String
+ * - created: String (YYYY-MM-DD)
+ * - status: String: one of ''=valid, r=revoked, e=expired
+ * - uid: Array of Strings with UIDs
+
+ */
+ async searchKeyserver(searchTerm, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(`keyserver.jsm: accessKeyBase: search()\n`);
+ let retObj = {
+ result: 0,
+ errorDetails: "",
+ pubKeys: [],
+ };
+
+ try {
+ let r = await this.accessKeyServer(
+ lazy.EnigmailConstants.SEARCH_KEY,
+ searchTerm,
+ listener
+ );
+
+ let res = JSON.parse(r);
+ let completions = res.completions;
+
+ for (let hit in completions) {
+ if (
+ completions[hit] &&
+ completions[hit].components.key_fingerprint !== undefined
+ ) {
+ let uid = completions[hit].components.username.val;
+ if ("full_name" in completions[hit].components) {
+ uid += " (" + completions[hit].components.full_name.val + ")";
+ }
+ let key = {
+ keyId:
+ completions[hit].components.key_fingerprint.val.toUpperCase(),
+ keyLen:
+ completions[hit].components.key_fingerprint.nbits.toString(),
+ keyType:
+ completions[hit].components.key_fingerprint.algo.toString(),
+ created: 0, //date.toDateString(),
+ uid: [uid],
+ status: "",
+ };
+ retObj.pubKeys.push(key);
+ }
+ }
+ } catch (ex) {
+ retObj.result = ex.result;
+ retObj.errorDetails = ex.errorDetails;
+ throw retObj;
+ }
+
+ return retObj;
+ },
+
+ upload() {
+ throw Components.Exception("", Cr.NS_ERROR_FAILURE);
+ },
+
+ refresh(keyServer, listener = null) {
+ lazy.EnigmailLog.DEBUG(`keyserver.jsm: accessKeyBase: refresh()\n`);
+ let keyList = lazy.EnigmailKeyRing.getAllKeys()
+ .keyList.map(keyObj => {
+ return "0x" + keyObj.fpr;
+ })
+ .join(" ");
+
+ return this.download(true, keyList, keyServer, listener);
+ },
+};
+
+function getAccessType(keyserver) {
+ if (!keyserver) {
+ throw new Error("getAccessType requires explicit keyserver parameter");
+ }
+
+ let srv = parseKeyserverUrl(keyserver);
+ switch (srv.protocol) {
+ case "keybase":
+ return accessKeyBase;
+ case "vks":
+ return accessVksServer;
+ }
+
+ if (srv.host.search(/keys.openpgp.org$/i) >= 0) {
+ return accessVksServer;
+ }
+
+ return accessHkpInternal;
+}
+
+/**
+ Object to handle VKS requests (for example keys.openpgp.org)
+ */
+const accessVksServer = {
+ /**
+ * Create the payload of VKS requests (currently upload only)
+ *
+ */
+ async buildJsonPayload(actionFlag, searchTerms, locale) {
+ switch (actionFlag) {
+ case lazy.EnigmailConstants.UPLOAD_KEY:
+ let exitCodeObj = {};
+ let keyData = await lazy.EnigmailKeyRing.extractPublicKeys(
+ ["0x" + searchTerms], // must be id or fingerprint
+ null,
+ null,
+ null,
+ exitCodeObj,
+ {}
+ );
+ if (exitCodeObj.value !== 0 || keyData.length === 0) {
+ return null;
+ }
+
+ return JSON.stringify({
+ keytext: keyData,
+ });
+
+ case lazy.EnigmailConstants.GET_CONFIRMATION_LINK:
+ return JSON.stringify({
+ token: searchTerms.token,
+ addresses: searchTerms.addresses,
+ locale: [locale],
+ });
+
+ case lazy.EnigmailConstants.DOWNLOAD_KEY:
+ case lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT:
+ case lazy.EnigmailConstants.SEARCH_KEY:
+ return "";
+ }
+
+ // other actions are not yet implemented
+ return null;
+ },
+
+ /**
+ * return the URL and the HTTP access method for a given action
+ */
+ createRequestUrl(keyserver, actionFlag, searchTerm) {
+ let keySrv = parseKeyserverUrl(keyserver);
+ let contentType = "text/plain;charset=UTF-8";
+
+ let method = "GET";
+
+ let url = "https://" + keySrv.host;
+
+ if (actionFlag === lazy.EnigmailConstants.UPLOAD_KEY) {
+ url += "/vks/v1/upload";
+ method = "POST";
+ contentType = "application/json";
+ } else if (actionFlag === lazy.EnigmailConstants.GET_CONFIRMATION_LINK) {
+ url += "/vks/v1/request-verify";
+ method = "POST";
+ contentType = "application/json";
+ } else if (
+ actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY ||
+ actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT ||
+ actionFlag === lazy.EnigmailConstants.SEARCH_KEY
+ ) {
+ if (searchTerm) {
+ let lookup = "/vks/";
+ if (searchTerm.indexOf("0x") === 0) {
+ searchTerm = searchTerm.substr(2);
+ if (
+ searchTerm.length == 16 &&
+ searchTerm.search(/^[A-F0-9]+$/) === 0
+ ) {
+ lookup = "/vks/v1/by-keyid/" + searchTerm;
+ } else if (
+ searchTerm.length == 40 &&
+ searchTerm.search(/^[A-F0-9]+$/) === 0
+ ) {
+ lookup = "/vks/v1/by-fingerprint/" + searchTerm;
+ }
+ } else {
+ try {
+ searchTerm = lazy.EnigmailFuncs.stripEmail(searchTerm);
+ } catch (x) {}
+ lookup = "/vks/v1/by-email/" + searchTerm;
+ }
+ url += lookup;
+ }
+ }
+
+ return {
+ url,
+ method,
+ contentType,
+ };
+ },
+
+ /**
+ * Upload, search or download keys from a keyserver
+ *
+ * @param actionFlag: Number - Keyserver Action Flags: from EnigmailConstants
+ * @param keyId: String - space-separated list of search terms or key IDs
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Number (Status-ID)>
+ */
+ async accessKeyServer(actionFlag, keyserver, keyId, listener) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessVksServer.accessKeyServer(${keyserver})\n`
+ );
+ if (keyserver === null) {
+ keyserver = "keys.openpgp.org";
+ }
+
+ let uiLocale = Services.locale.appLocalesAsBCP47[0];
+ let payLoad = await this.buildJsonPayload(actionFlag, keyId, uiLocale);
+
+ return new Promise((resolve, reject) => {
+ let xmlReq = null;
+ if (listener && typeof listener === "object") {
+ listener.onCancel = function () {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessVksServer.accessKeyServer - onCancel() called\n`
+ );
+ if (xmlReq) {
+ xmlReq.abort();
+ }
+ reject(createError(lazy.EnigmailConstants.KEYSERVER_ERR_ABORTED));
+ };
+ }
+ if (actionFlag === lazy.EnigmailConstants.REFRESH_KEY) {
+ // we don't (need to) distinguish between refresh and download for our internal protocol
+ actionFlag = lazy.EnigmailConstants.DOWNLOAD_KEY;
+ }
+
+ if (payLoad === null) {
+ reject(createError(lazy.EnigmailConstants.KEYSERVER_ERR_UNKNOWN));
+ return;
+ }
+
+ xmlReq = new XMLHttpRequest();
+
+ xmlReq.onload = function () {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessVksServer.onload(): status=" +
+ xmlReq.status +
+ "\n"
+ );
+ switch (actionFlag) {
+ case lazy.EnigmailConstants.UPLOAD_KEY:
+ case lazy.EnigmailConstants.GET_CONFIRMATION_LINK:
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessVksServer.onload: " +
+ xmlReq.responseText +
+ "\n"
+ );
+ if (xmlReq.status >= 400) {
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ resolve(xmlReq.responseText);
+ }
+ return;
+
+ case lazy.EnigmailConstants.SEARCH_KEY:
+ if (xmlReq.status === 404) {
+ // key not found
+ resolve("");
+ } else if (xmlReq.status >= 400) {
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ resolve(xmlReq.responseText);
+ }
+ return;
+
+ case lazy.EnigmailConstants.DOWNLOAD_KEY:
+ case lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT:
+ if (xmlReq.status >= 400 && xmlReq.status < 500) {
+ // key not found
+ resolve(1);
+ } else if (xmlReq.status >= 500) {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessVksServer.onload: " +
+ xmlReq.responseText +
+ "\n"
+ );
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_ERROR)
+ );
+ } else {
+ let errorMsgObj = {},
+ importedKeysObj = {};
+ if (actionFlag === lazy.EnigmailConstants.DOWNLOAD_KEY) {
+ let r = lazy.EnigmailKeyRing.importKey(
+ null,
+ false,
+ xmlReq.responseText,
+ false,
+ "",
+ errorMsgObj,
+ importedKeysObj
+ );
+ if (r === 0) {
+ resolve(importedKeysObj.value);
+ } else {
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_IMPORT_ERROR
+ )
+ );
+ }
+ } else {
+ // DOWNLOAD_KEY_NO_IMPORT
+ resolve(xmlReq.responseText);
+ }
+ }
+ return;
+ }
+ resolve(-1);
+ };
+
+ xmlReq.onerror = function (e) {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessVksServer.accessKeyServer: onerror: " + e + "\n"
+ );
+ let err = lazy.FeedUtils.createTCPErrorFromFailedXHR(e.target);
+ switch (err.type) {
+ case "SecurityCertificate":
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_CERTIFICATE_ERROR
+ )
+ );
+ break;
+ case "SecurityProtocol":
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SECURITY_ERROR)
+ );
+ break;
+ case "Network":
+ reject(
+ createError(
+ lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_UNAVAILABLE
+ )
+ );
+ break;
+ }
+ reject(
+ createError(lazy.EnigmailConstants.KEYSERVER_ERR_SERVER_UNAVAILABLE)
+ );
+ };
+
+ xmlReq.onloadend = function () {
+ lazy.EnigmailLog.DEBUG(
+ "keyserver.jsm: accessVksServer.accessKeyServer: loadEnd\n"
+ );
+ };
+
+ let { url, method, contentType } = this.createRequestUrl(
+ keyserver,
+ actionFlag,
+ keyId
+ );
+
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessVksServer.accessKeyServer: requesting ${method} for ${url}\n`
+ );
+ xmlReq.open(method, url);
+ xmlReq.setRequestHeader("Content-Type", contentType);
+ xmlReq.send(payLoad);
+ });
+ },
+
+ /**
+ * Download keys from a keyserver
+ *
+ * @param keyIDs: String - space-separated list of search terms or key IDs
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<...>
+ */
+ async download(autoImport, keyIDs, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessVksServer.download(${keyIDs})\n`
+ );
+ let keyIdArr = keyIDs.split(/ +/);
+ let retObj = {
+ result: 0,
+ errorDetails: "",
+ keyList: [],
+ };
+
+ for (let i = 0; i < keyIdArr.length; i++) {
+ try {
+ let r = await this.accessKeyServer(
+ autoImport
+ ? lazy.EnigmailConstants.DOWNLOAD_KEY
+ : lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT,
+ keyserver,
+ keyIdArr[i],
+ listener
+ );
+ if (autoImport) {
+ if (Array.isArray(r)) {
+ retObj.keyList = retObj.keyList.concat(r);
+ }
+ } else if (typeof r == "string") {
+ retObj.keyData = r;
+ } else {
+ retObj.result = r;
+ }
+ } catch (ex) {
+ retObj.result = ex.result;
+ retObj.errorDetails = ex.errorDetails;
+ throw retObj;
+ }
+
+ if (listener && "onProgress" in listener) {
+ listener.onProgress(((i + 1) / keyIdArr.length) * 100);
+ }
+ }
+
+ return retObj;
+ },
+
+ refresh(keyServer, listener = null) {
+ let keyList = lazy.EnigmailKeyRing.getAllKeys()
+ .keyList.map(keyObj => {
+ return "0x" + keyObj.fpr;
+ })
+ .join(" ");
+
+ return this.download(true, keyList, keyServer, listener);
+ },
+
+ async requestConfirmationLink(keyserver, jsonFragment) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessVksServer.requestConfirmationLink()\n`
+ );
+
+ let response = JSON.parse(jsonFragment);
+
+ let addr = [];
+
+ for (let email in response.status) {
+ if (response.status[email] !== "published") {
+ addr.push(email);
+ }
+ }
+
+ if (addr.length > 0) {
+ let r = await this.accessKeyServer(
+ lazy.EnigmailConstants.GET_CONFIRMATION_LINK,
+ keyserver,
+ {
+ token: response.token,
+ addresses: addr,
+ },
+ null
+ );
+
+ if (typeof r === "string") {
+ return addr.length;
+ }
+ }
+
+ return 0;
+ },
+
+ /**
+ * Upload keys to a keyserver
+ *
+ * @param keyIDs: String - space-separated list of search terms or key IDs
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return {boolean} - Returns true if the key was sent successfully
+ */
+ async upload(keyIDs, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessVksServer.upload(${keyIDs})\n`
+ );
+ let keyIdArr = keyIDs.split(/ +/);
+ let rv = false;
+
+ for (let i = 0; i < keyIdArr.length; i++) {
+ let keyObj = lazy.EnigmailKeyRing.getKeyById(keyIdArr[i]);
+
+ if (!keyObj.secretAvailable) {
+ throw new Error(
+ "public keyserver uploading supported only for user's own keys"
+ );
+ }
+
+ try {
+ let r = await this.accessKeyServer(
+ lazy.EnigmailConstants.UPLOAD_KEY,
+ keyserver,
+ keyIdArr[i],
+ listener
+ );
+ if (typeof r === "string") {
+ let req = await this.requestConfirmationLink(keyserver, r);
+ if (req >= 0) {
+ rv = true;
+ }
+ } else {
+ rv = false;
+ break;
+ }
+ } catch (ex) {
+ console.log(ex.errorDetails);
+ rv = false;
+ break;
+ }
+
+ if (listener && "onProgress" in listener) {
+ listener.onProgress(((i + 1) / keyIdArr.length) * 100);
+ }
+ }
+
+ return rv;
+ },
+
+ /**
+ * Search for keys on a keyserver
+ *
+ * @param searchTerm: String - search term
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Object>
+ * - result: Number
+ * - pubKeys: Array of Object:
+ * PubKeys: Object with:
+ * - keyId: String
+ * - keyLen: String
+ * - keyType: String
+ * - created: String (YYYY-MM-DD)
+ * - status: String: one of ''=valid, r=revoked, e=expired
+ * - uid: Array of Strings with UIDs
+ */
+ async searchKeyserver(searchTerm, keyserver, listener = null) {
+ lazy.EnigmailLog.DEBUG(
+ `keyserver.jsm: accessVksServer.search(${searchTerm})\n`
+ );
+ let retObj = {
+ result: 0,
+ errorDetails: "",
+ pubKeys: [],
+ };
+ let key = null;
+
+ let searchArr = searchTerm.split(/ +/);
+
+ try {
+ for (let i in searchArr) {
+ let r = await this.accessKeyServer(
+ lazy.EnigmailConstants.SEARCH_KEY,
+ keyserver,
+ searchArr[i],
+ listener
+ );
+
+ const cApi = lazy.EnigmailCryptoAPI();
+ let keyList = await cApi.getKeyListFromKeyBlockAPI(
+ r,
+ true,
+ false,
+ true,
+ false
+ );
+ if (!keyList) {
+ retObj.result = -1;
+ // TODO: should we set retObj.errorDetails to a string?
+ return retObj;
+ }
+
+ for (let k in keyList) {
+ key = {
+ keyId: keyList[k].fpr,
+ keyLen: "0",
+ keyType: "",
+ created: keyList[k].created,
+ uid: [keyList[k].name],
+ status: keyList[k].revoke ? "r" : "",
+ };
+
+ for (let uid of keyList[k].uids) {
+ key.uid.push(uid);
+ }
+
+ retObj.pubKeys.push(key);
+ }
+ }
+ } catch (ex) {
+ retObj.result = ex.result;
+ retObj.errorDetails = ex.errorDetails;
+ throw retObj;
+ }
+
+ return retObj;
+ },
+};
+
+var EnigmailKeyServer = {
+ /**
+ * Download keys from a keyserver
+ *
+ * @param keyIDs: String - space-separated list of FPRs or key IDs
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Object>
+ * Object: - result: Number - result Code (0 = OK),
+ * - keyList: Array of String - imported key FPR
+ */
+ async download(keyIDs, keyserver = null, listener) {
+ let acc = getAccessType(keyserver);
+ return acc.download(true, keyIDs, keyserver, listener);
+ },
+
+ async downloadNoImport(keyIDs, keyserver = null, listener) {
+ let acc = getAccessType(keyserver);
+ return acc.download(false, keyIDs, keyserver, listener);
+ },
+
+ serverReqURL(keyIDs, keyserver) {
+ let acc = getAccessType(keyserver);
+ let { url } = acc.createRequestUrl(
+ keyserver,
+ lazy.EnigmailConstants.DOWNLOAD_KEY_NO_IMPORT,
+ keyIDs
+ );
+ return url;
+ },
+
+ /**
+ * Upload keys to a keyserver
+ *
+ * @param keyIDs: String - space-separated list of key IDs or FPR
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return {boolean} - Returns true if the key was sent successfully
+ */
+
+ async upload(keyIDs, keyserver = null, listener) {
+ let acc = getAccessType(keyserver);
+ return acc.upload(keyIDs, keyserver, listener);
+ },
+
+ /**
+ * Search keys on a keyserver
+ *
+ * @param searchString: String - search term. Multiple email addresses can be search by spaces
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<Object>
+ * - result: Number
+ * - pubKeys: Array of Object:
+ * PubKeys: Object with:
+ * - keyId: String
+ * - keyLen: String
+ * - keyType: String
+ * - created: String (YYYY-MM-DD)
+ * - status: String: one of ''=valid, r=revoked, e=expired
+ * - uid: Array of Strings with UIDs
+ */
+ async searchKeyserver(searchString, keyserver = null, listener) {
+ let acc = getAccessType(keyserver);
+ return acc.search(searchString, keyserver, listener);
+ },
+
+ async searchAndDownloadSingleResultNoImport(
+ searchString,
+ keyserver = null,
+ listener
+ ) {
+ let acc = getAccessType(keyserver);
+ let searchResult = await acc.searchKeyserver(
+ searchString,
+ keyserver,
+ listener
+ );
+ if (searchResult.result != 0 || searchResult.pubKeys.length != 1) {
+ return null;
+ }
+ return this.downloadNoImport(
+ searchResult.pubKeys[0].keyId,
+ keyserver,
+ listener
+ );
+ },
+
+ /**
+ * Refresh all keys
+ *
+ * @param keyserver: String - keyserver URL (optionally incl. protocol)
+ * @param listener: optional Object implementing the KeySrvListener API (above)
+ *
+ * @return: Promise<resultStatus> (identical to download)
+ */
+ refresh(keyserver = null, listener) {
+ let acc = getAccessType(keyserver);
+ return acc.refresh(keyserver, listener);
+ },
+};