tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CryptoSafetyParent.sys.mjs (3755B)


      1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
      2 /* Copyright (c) 2020, The Tor Project, Inc.
      3 *
      4 * This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      9 
     10 const lazy = {};
     11 
     12 ChromeUtils.defineESModuleGetters(lazy, {
     13  TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
     14  Bech32Decode: "resource://gre/modules/Bech32Decode.sys.mjs",
     15 });
     16 
     17 ChromeUtils.defineLazyGetter(lazy, "CryptoStrings", function () {
     18  return new Localization(["toolkit/global/tor-browser.ftl"]);
     19 });
     20 
     21 XPCOMUtils.defineLazyPreferenceGetter(
     22  lazy,
     23  "isCryptoSafetyEnabled",
     24  "security.cryptoSafety",
     25  true // Defaults to true.
     26 );
     27 
     28 function looksLikeCryptoAddress(s) {
     29  // P2PKH and P2SH addresses
     30  // https://stackoverflow.com/a/24205650
     31  const bitcoinAddr = /^[13][a-km-zA-HJ-NP-Z1-9]{25,39}$/;
     32  if (bitcoinAddr.test(s)) {
     33    return true;
     34  }
     35 
     36  // Bech32 addresses
     37  if (lazy.Bech32Decode(s) !== null) {
     38    return true;
     39  }
     40 
     41  // regular addresses
     42  const etherAddr = /^0x[a-fA-F0-9]{40}$/;
     43  if (etherAddr.test(s)) {
     44    return true;
     45  }
     46 
     47  // t-addresses
     48  // https://www.reddit.com/r/zec/comments/8mxj6x/simple_regex_to_validate_a_zcash_tz_address/dzr62p5/
     49  const zcashAddr = /^t1[a-zA-Z0-9]{33}$/;
     50  if (zcashAddr.test(s)) {
     51    return true;
     52  }
     53 
     54  // Standard, Integrated, and 256-bit Integrated addresses
     55  // https://monero.stackexchange.com/a/10627
     56  const moneroAddr =
     57    /^4(?:[0-9AB]|[1-9A-HJ-NP-Za-km-z]{12}(?:[1-9A-HJ-NP-Za-km-z]{30})?)[1-9A-HJ-NP-Za-km-z]{93}$/;
     58  if (moneroAddr.test(s)) {
     59    return true;
     60  }
     61 
     62  return false;
     63 }
     64 
     65 export class CryptoSafetyParent extends JSWindowActorParent {
     66  async receiveMessage(aMessage) {
     67    if (
     68      !lazy.isCryptoSafetyEnabled ||
     69      aMessage.name !== "CryptoSafety:CopiedText"
     70    ) {
     71      return;
     72    }
     73 
     74    // Read the global clipboard. We assume the contents come from the HTTP
     75    // page specified in `aMessage.data.host`.
     76    const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
     77      Ci.nsITransferable
     78    );
     79    trans.init(null);
     80    trans.addDataFlavor("text/plain");
     81    Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
     82    let data = {};
     83    trans.getTransferData("text/plain", data);
     84    data = data?.value.QueryInterface(Ci.nsISupportsString).data;
     85 
     86    let address = data?.replace(/\s+/g, "");
     87 
     88    if (!address || !looksLikeCryptoAddress(address)) {
     89      return;
     90    }
     91 
     92    if (address.length > 32) {
     93      address = `${address.substring(0, 32)}…`;
     94    }
     95 
     96    const [titleText, bodyText, reloadText, dismissText] =
     97      await lazy.CryptoStrings.formatValues([
     98        { id: "crypto-safety-prompt-title" },
     99        {
    100          id: "crypto-safety-prompt-body",
    101          args: { address, host: aMessage.data.host },
    102        },
    103        { id: "crypto-safety-prompt-reload-button" },
    104        { id: "crypto-safety-prompt-dismiss-button" },
    105      ]);
    106 
    107    const buttonPressed = Services.prompt.confirmEx(
    108      this.browsingContext.topChromeWindow,
    109      titleText,
    110      bodyText,
    111      Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
    112        Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1,
    113      reloadText,
    114      dismissText,
    115      null,
    116      null,
    117      {}
    118    );
    119 
    120    if (buttonPressed === 0) {
    121      const { browsingContext } = this.manager;
    122      const browser = browsingContext.embedderElement;
    123      if (browser) {
    124        lazy.TorDomainIsolator.newCircuitForBrowser(
    125          browser.ownerGlobal.gBrowser.selectedBrowser
    126        );
    127      }
    128    }
    129  }
    130 }