diff options
Diffstat (limited to 'comm/mail/extensions/openpgp/content/modules/wkdLookup.jsm')
-rw-r--r-- | comm/mail/extensions/openpgp/content/modules/wkdLookup.jsm | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/comm/mail/extensions/openpgp/content/modules/wkdLookup.jsm b/comm/mail/extensions/openpgp/content/modules/wkdLookup.jsm new file mode 100644 index 0000000000..bf5fd25845 --- /dev/null +++ b/comm/mail/extensions/openpgp/content/modules/wkdLookup.jsm @@ -0,0 +1,363 @@ +/* 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"; + +/** + * Lookup keys by email addresses using WKD. A an email address is lookep up at most + * once a day. (see https://tools.ietf.org/html/draft-koch-openpgp-webkey-service) + */ + +var EXPORTED_SYMBOLS = ["EnigmailWkdLookup"]; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +const lazy = {}; + +XPCOMUtils.defineLazyModuleGetters(lazy, { + DNS: "resource:///modules/DNS.jsm", + EnigmailData: "chrome://openpgp/content/modules/data.jsm", + EnigmailLog: "chrome://openpgp/content/modules/log.jsm", + EnigmailZBase32: "chrome://openpgp/content/modules/zbase32.jsm", +}); + +// Those domains are not expected to have WKD: +var EXCLUDE_DOMAINS = [ + /* Default domains included */ + "aol.com", + "att.net", + "comcast.net", + "facebook.com", + "gmail.com", + "gmx.com", + "googlemail.com", + "google.com", + "hotmail.com", + "hotmail.co.uk", + "mac.com", + "me.com", + "mail.com", + "msn.com", + "live.com", + "sbcglobal.net", + "verizon.net", + "yahoo.com", + "yahoo.co.uk", + + /* Other global domains */ + "email.com", + "games.com" /* AOL */, + "gmx.net", + "icloud.com", + "iname.com", + "inbox.com", + "lavabit.com", + "love.com" /* AOL */, + "outlook.com", + "pobox.com", + "tutanota.de", + "tutanota.com", + "tutamail.com", + "tuta.io", + "keemail.me", + "rocketmail.com" /* Yahoo */, + "safe-mail.net", + "wow.com" /* AOL */, + "ygm.com" /* AOL */, + "ymail.com" /* Yahoo */, + "zoho.com", + "yandex.com", + + /* United States ISP domains */ + "bellsouth.net", + "charter.net", + "cox.net", + "earthlink.net", + "juno.com", + + /* British ISP domains */ + "btinternet.com", + "virginmedia.com", + "blueyonder.co.uk", + "freeserve.co.uk", + "live.co.uk", + "ntlworld.com", + "o2.co.uk", + "orange.net", + "sky.com", + "talktalk.co.uk", + "tiscali.co.uk", + "virgin.net", + "wanadoo.co.uk", + "bt.com", + + /* Domains used in Asia */ + "sina.com", + "sina.cn", + "qq.com", + "naver.com", + "hanmail.net", + "daum.net", + "nate.com", + "yahoo.co.jp", + "yahoo.co.kr", + "yahoo.co.id", + "yahoo.co.in", + "yahoo.com.sg", + "yahoo.com.ph", + "163.com", + "yeah.net", + "126.com", + "21cn.com", + "aliyun.com", + "foxmail.com", + + /* French ISP domains */ + "hotmail.fr", + "live.fr", + "laposte.net", + "yahoo.fr", + "wanadoo.fr", + "orange.fr", + "gmx.fr", + "sfr.fr", + "neuf.fr", + "free.fr", + + /* German ISP domains */ + "gmx.de", + "hotmail.de", + "live.de", + "online.de", + "t-online.de" /* T-Mobile */, + "web.de", + "yahoo.de", + + /* Italian ISP domains */ + "libero.it", + "virgilio.it", + "hotmail.it", + "aol.it", + "tiscali.it", + "alice.it", + "live.it", + "yahoo.it", + "email.it", + "tin.it", + "poste.it", + "teletu.it", + + /* Russian ISP domains */ + "mail.ru", + "rambler.ru", + "yandex.ru", + "ya.ru", + "list.ru", + + /* Belgian ISP domains */ + "hotmail.be", + "live.be", + "skynet.be", + "voo.be", + "tvcablenet.be", + "telenet.be", + + /* Argentinian ISP domains */ + "hotmail.com.ar", + "live.com.ar", + "yahoo.com.ar", + "fibertel.com.ar", + "speedy.com.ar", + "arnet.com.ar", + + /* Domains used in Mexico */ + "yahoo.com.mx", + "live.com.mx", + "hotmail.es", + "hotmail.com.mx", + "prodigy.net.mx", + + /* Domains used in Canada */ + "yahoo.ca", + "hotmail.ca", + "bell.net", + "shaw.ca", + "sympatico.ca", + "rogers.com", + + /* Domains used in Brazil */ + "yahoo.com.br", + "hotmail.com.br", + "outlook.com.br", + "uol.com.br", + "bol.com.br", + "terra.com.br", + "ig.com.br", + "itelefonica.com.br", + "r7.com", + "zipmail.com.br", + "globo.com", + "globomail.com", + "oi.com.br", +]; + +var EnigmailWkdLookup = { + /** + * get the download URL for an email address for WKD or domain-specific locations + * + * @param {string} email: email address + * + * @returns {Promise<string>}: URL (or null if not possible) + */ + async getDownloadUrlFromEmail(email, advancedMethod) { + email = email.toLowerCase().trim(); + + let url = await getSiteSpecificUrl(email); + if (url) { + return url; + } + + let at = email.indexOf("@"); + + let domain = email.substr(at + 1); + let user = email.substr(0, at); + + let data = [...new TextEncoder().encode(user)]; + let ch = Cc["@mozilla.org/security/hash;1"].createInstance( + Ci.nsICryptoHash + ); + ch.init(ch.SHA1); + ch.update(data, data.length); + let gotHash = ch.finish(false); + let encodedHash = lazy.EnigmailZBase32.encode(gotHash); + + if (advancedMethod) { + url = + "https://openpgpkey." + + domain + + "/.well-known/openpgpkey/" + + domain + + "/hu/" + + encodedHash + + "?l=" + + escape(user); + } else { + url = + "https://" + + domain + + "/.well-known/openpgpkey/hu/" + + encodedHash + + "?l=" + + escape(user); + } + + return url; + }, + + /** + * Download a key for an email address + * + * @param {string} email: email address + * @param {string} url: url from getDownloadUrlFromEmail() + * + * @returns {Promise<string>}: Key data (or null if not possible) + */ + async downloadKey(url) { + let padLen = (url.length % 512) + 1; + let hdrs = new Headers({ + Authorization: "Basic " + btoa("no-user:"), + }); + hdrs.append("Content-Type", "application/octet-stream"); + hdrs.append("X-Enigmail-Padding", "x".padEnd(padLen, "x")); + + let myRequest = new Request(url, { + method: "GET", + headers: hdrs, + mode: "cors", + //redirect: 'error', + redirect: "follow", + cache: "default", + }); + + let response; + try { + lazy.EnigmailLog.DEBUG( + "wkdLookup.jsm: downloadKey: requesting " + url + "\n" + ); + response = await fetch(myRequest); + if (!response.ok) { + return null; + } + } catch (ex) { + lazy.EnigmailLog.DEBUG( + "wkdLookup.jsm: downloadKey: error " + ex.toString() + "\n" + ); + return null; + } + + try { + if ( + response.headers.has("content-type") && + response.headers.get("content-type").search(/^text\/html/i) === 0 + ) { + // if we get HTML output, we return nothing (for example redirects to error catching pages) + return null; + } + return lazy.EnigmailData.arrayBufferToString( + Cu.cloneInto(await response.arrayBuffer(), {}) + ); + } catch (ex) { + lazy.EnigmailLog.DEBUG( + "wkdLookup.jsm: downloadKey: error " + ex.toString() + "\n" + ); + return null; + } + }, + + isWkdAvailable(email) { + let domain = email.toLowerCase().replace(/^.*@/, ""); + + return !EXCLUDE_DOMAINS.includes(domain); + }, +}; + +/** + * Get special URLs for specific sites that don't use WKD, but still provide + * public keys of their users in + * + * @param {string}: emailAddr: email address in lowercase + * + * @returns {Promise<string>}: URL or null of no URL relevant + */ +async function getSiteSpecificUrl(emailAddr) { + let domain = emailAddr.replace(/^.+@/, ""); + let url = null; + + switch (domain) { + case "protonmail.ch": + case "protonmail.com": + case "pm.me": + url = + "https://api.protonmail.ch/pks/lookup?op=get&options=mr&search=" + + escape(emailAddr); + break; + } + if (!url) { + let records = await lazy.DNS.mx(domain); + const mxHosts = records.filter(record => record.host); + + if ( + mxHosts && + (mxHosts.includes("mail.protonmail.ch") || + mxHosts.includes("mailsec.protonmail.ch")) + ) { + url = + "https://api.protonmail.ch/pks/lookup?op=get&options=mr&search=" + + escape(emailAddr); + } + } + return url; +} |