gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-webex] 02/02: remove dependency in taler-wall


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] 02/02: remove dependency in taler-wallet-lib, implement pay signature check/storage
Date: Wed, 26 Apr 2017 03:10:58 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-webex.

commit 79a2eed5589468c2df3e4ee2d190d9fc43b80fe9
Author: Florian Dold <address@hidden>
AuthorDate: Wed Apr 26 03:10:52 2017 +0200

    remove dependency in taler-wallet-lib, implement pay signature check/storage
---
 manifest.json                 |   1 -
 node_modules/.yarn-integrity  |   2 +-
 src/content_scripts/notify.ts | 768 +++++++++++++++++++++++++-----------------
 src/cryptoApi.ts              |   4 +
 src/cryptoWorker.ts           |  14 +
 src/emscriptif.ts             |  21 ++
 src/taler-wallet-lib.ts       |   1 -
 src/wallet.ts                 |   9 +-
 src/wxBackend.ts              |   6 +-
 tsconfig.json                 |   1 -
 web-common                    |   2 +-
 11 files changed, 514 insertions(+), 315 deletions(-)

diff --git a/manifest.json b/manifest.json
index f59aac2..c93d49f 100644
--- a/manifest.json
+++ b/manifest.json
@@ -43,7 +43,6 @@
     {
       "matches": ["*://*/*"],
       "js": [
-        "src/taler-wallet-lib.js",
         "dist/contentScript-bundle.js"
       ],
       "run_at": "document_start"
diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity
index a45758d..6d4c42d 100644
--- a/node_modules/.yarn-integrity
+++ b/node_modules/.yarn-integrity
@@ -1 +1 @@
-0dc42bcf25ec3d59c7bd394b1d9f751da1a3446ef6012260b277831cef6de2bf
\ No newline at end of file
+751d3ff225403bea12799f2c0ad32d26a0ff81a4f88821c8f1615d3ddc5a9533
\ No newline at end of file
diff --git a/src/content_scripts/notify.ts b/src/content_scripts/notify.ts
index 8a25dd5..a0f76cf 100644
--- a/src/content_scripts/notify.ts
+++ b/src/content_scripts/notify.ts
@@ -23,378 +23,530 @@
  */
 
 
-"use strict";
-
 import URI = require("urijs");
 
 declare var cloneInto: any;
 
-// Make sure we don't pollute the namespace too much.
-namespace TalerNotify {
-  const PROTOCOL_VERSION = 1;
+const PROTOCOL_VERSION = 1;
 
-  let logVerbose: boolean = false;
-  try {
-    logVerbose = !!localStorage.getItem("taler-log-verbose");
-  } catch (e) {
-    // can't read from local storage
-  }
+let logVerbose: boolean = false;
+try {
+  logVerbose = !!localStorage.getItem("taler-log-verbose");
+} catch (e) {
+  // can't read from local storage
+}
 
-  if (!taler) {
-    console.error("Taler wallet lib not included, HTTP 402 payments not" +
-                  " supported");
-  }
+if (document.documentElement.getAttribute("data-taler-nojs")) {
+  document.dispatchEvent(new Event("taler-probe-result"));
+}
 
-  if (document.documentElement.getAttribute("data-taler-nojs")) {
-    document.dispatchEvent(new Event("taler-probe-result"));
-  }
 
+function subst(url: string, H_contract: string) {
+  url = url.replace("${H_contract}", H_contract);
+  url = url.replace("${$}", "$");
+  return url;
+}
 
-  function subst(url: string, H_contract: string) {
-    url = url.replace("${H_contract}", H_contract);
-    url = url.replace("${$}", "$");
-    return url;
-  }
+interface Handler {
+  type: string;
+  listener: (e: CustomEvent) => void|Promise<void>;
+}
+const handlers: Handler[] = [];
+
+function hashContract(contract: string): Promise<string> {
+  let walletHashContractMsg = {
+    type: "hash-contract",
+    detail: {contract}
+  };
+  return new Promise<string>((resolve, reject) => {
+    chrome.runtime.sendMessage(walletHashContractMsg, (resp: any) => {
+      if (!resp.hash) {
+        console.log("error", resp);
+        reject(Error("hashing failed"));
+      }
+      resolve(resp.hash);
+    });
+  });
+}
 
-  interface Handler {
-    type: string;
-    listener: (e: CustomEvent) => void|Promise<void>;
-  }
-  const handlers: Handler[] = [];
+function queryPayment(url: string): Promise<any> {
+  const walletMsg = {
+    type: "query-payment",
+    detail: { url },
+  };
+  return new Promise((resolve, reject) => {
+    chrome.runtime.sendMessage(walletMsg, (resp: any) => {
+      resolve(resp);
+    });
+  });
+}
 
-  function hashContract(contract: string): Promise<string> {
-    let walletHashContractMsg = {
-      type: "hash-contract",
-      detail: {contract}
-    };
-    return new Promise<string>((resolve, reject) => {
-      chrome.runtime.sendMessage(walletHashContractMsg, (resp: any) => {
-        if (!resp.hash) {
-          console.log("error", resp);
-          reject(Error("hashing failed"));
-        }
-        resolve(resp.hash);
-      });
+function putHistory(historyEntry: any): Promise<void> {
+  const walletMsg = {
+    type: "put-history-entry",
+    detail: {
+      historyEntry,
+    },
+  };
+  return new Promise<void>((resolve, reject) => {
+    chrome.runtime.sendMessage(walletMsg, (resp: any) => {
+      resolve();
     });
-  }
+  });
+}
 
-  function queryPayment(url: string): Promise<any> {
-    const walletMsg = {
-      type: "query-payment",
-      detail: { url },
-    };
-    return new Promise((resolve, reject) => {
-      chrome.runtime.sendMessage(walletMsg, (resp: any) => {
+function saveOffer(offer: any): Promise<number> {
+  const walletMsg = {
+    type: "save-offer",
+    detail: {
+      offer: {
+        contract: offer.data,
+        merchant_sig: offer.sig,
+        H_contract: offer.hash,
+        offer_time: new Date().getTime() / 1000
+      },
+    },
+  };
+  return new Promise<number>((resolve, reject) => {
+    chrome.runtime.sendMessage(walletMsg, (resp: any) => {
+      if (resp && resp.error) {
+        reject(resp);
+      } else {
         resolve(resp);
-      });
+      }
     });
+  });
+}
+
+
+
+
+let sheet: CSSStyleSheet|null;
+
+function initStyle() {
+  logVerbose && console.log("taking over styles");
+  const name = "taler-presence-stylesheet";
+  const content = "/* Taler stylesheet controlled by JS */";
+  let style = document.getElementById(name) as HTMLStyleElement|null; 
+  if (!style) {
+    style = document.createElement("style");
+    // Needed by WebKit
+    style.appendChild(document.createTextNode(content));
+    style.id = name;
+    document.head.appendChild(style);
+    sheet = style.sheet as CSSStyleSheet;
+  } else {
+    // We've taken over the stylesheet now,
+    // make it clear by clearing all the rules in it
+    // and making it obvious in the DOM.
+    if (style.tagName.toLowerCase() === "style") {
+      style.innerText = content;
+    }
+    if (!style.sheet) {
+      throw Error("taler-presence-stylesheet should be a style sheet (<link> 
or <style>)");
+    }
+    sheet = style.sheet as CSSStyleSheet;
+    while (sheet.cssRules.length > 0) {
+      sheet.deleteRule(0);
+    }
   }
+}
 
-  function putHistory(historyEntry: any): Promise<void> {
-    const walletMsg = {
-      type: "put-history-entry",
-      detail: {
-        historyEntry,
-      },
-    };
-    return new Promise<void>((resolve, reject) => {
-      chrome.runtime.sendMessage(walletMsg, (resp: any) => {
-        resolve();
-      });
-    });
+
+function setStyles(installed: boolean) {
+  if (!sheet || !sheet.cssRules) {
+    return;
+  }
+  while (sheet.cssRules.length > 0) {
+    sheet.deleteRule(0);
   }
+  if (installed) {
+    sheet.insertRule(".taler-installed-hide { display: none; }", 0);
+    sheet.insertRule(".taler-probed-hide { display: none; }", 0);
+  } else {
+    sheet.insertRule(".taler-installed-show { display: none; }", 0);
+  }
+}
 
-  function saveOffer(offer: any): Promise<number> {
-    const walletMsg = {
-      type: "save-offer",
-      detail: {
-        offer: {
-          contract: offer.data,
-          merchant_sig: offer.sig,
-          H_contract: offer.hash,
-          offer_time: new Date().getTime() / 1000
-        },
-      },
-    };
-    return new Promise<number>((resolve, reject) => {
-      chrome.runtime.sendMessage(walletMsg, (resp: any) => {
-        if (resp && resp.error) {
-          reject(resp);
-        } else {
-          resolve(resp);
-        }
-      });
-    });
+
+
+function handlePaymentResponse(walletResp: any) {
+  /**
+   * Handle a failed payment.
+   *
+   * Try to notify the wallet first, before we show a potentially
+   * synchronous error message (such as an alert) or leave the page.
+   */
+  function handleFailedPayment(r: XMLHttpRequest) {
+    let timeoutHandle: number|null = null;
+    function err() {
+      // FIXME: proper error reporting!
+      console.log("pay-failed", {status: r.status, response: r.responseText});
+    }
+    function onTimeout() {
+      timeoutHandle = null
+      err();
+    }
+    talerPaymentFailed(walletResp.H_contract).then(() => {
+      if (timeoutHandle != null) {
+        clearTimeout(timeoutHandle);
+        timeoutHandle = null;
+      }
+      err();
+    })
+    timeoutHandle = setTimeout(onTimeout, 200);
   }
 
-  function init() {
-    chrome.runtime.sendMessage({type: "get-tab-cookie"}, (resp) => {
-      if (chrome.runtime.lastError) {
-        logVerbose && console.log("extension not yet ready");
-        window.setTimeout(init, 200);
+
+  logVerbose && console.log("handling taler-notify-payment: ", walletResp);
+  // Payment timeout in ms.
+  let timeout_ms = 1000;
+  // Current request.
+  let r: XMLHttpRequest|null;
+  let timeoutHandle: number|null = null;
+  function sendPay() {
+    r = new XMLHttpRequest();
+    r.open("post", walletResp.contract.pay_url);
+    r.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+    r.send(JSON.stringify(walletResp.payReq));
+    r.onload = function() {
+      if (!r) {
         return;
       }
-      registerHandlers();
-      // Hack to know when the extension is unloaded
-      let port = chrome.runtime.connect();
-
-      port.onDisconnect.addListener(() => {
-        logVerbose && console.log("chrome runtime disconnected, removing 
handlers");
-        for (let handler of handlers) {
-          document.removeEventListener(handler.type, handler.listener);
-        }
-      });
+      switch (r.status) {
+        case 200:
+          const merchantResp = JSON.parse(r.responseText);
+          logVerbose && console.log("got success from pay_url");
+          talerPaymentSucceeded({H_contract: walletResp.H_contract, 
merchantSig: merchantResp.sig}).then(() => {
+            let nextUrl = walletResp.contract.fulfillment_url;
+            logVerbose && console.log("taler-payment-succeeded done, going 
to", nextUrl);
+            window.location.href = nextUrl;
+            window.location.reload(true);
+          });
+          break;
+        default:
+          handleFailedPayment(r);
+          break;
+      }
+      r = null;
+      if (timeoutHandle != null) {
+        clearTimeout(timeoutHandle!);
+        timeoutHandle = null;
+      }
+    };
+    function retry() {
+      if (r) {
+        r.abort();
+        r = null;
+      }
+      timeout_ms = Math.min(timeout_ms * 2, 10 * 1000);
+      logVerbose && console.log("sendPay timed out, retrying in ", timeout_ms, 
"ms");
+      sendPay();
+    }
+    timeoutHandle = setTimeout(retry, timeout_ms);
+  }
+  sendPay();
+}
 
-      if (resp && resp.type == "pay") {
-        logVerbose && console.log("doing taler.pay with", resp.payDetail);
-        taler.internalPay(resp.payDetail);
-        document.documentElement.style.visibility = "hidden";
+
+function init() {
+  chrome.runtime.sendMessage({type: "get-tab-cookie"}, (resp) => {
+    if (chrome.runtime.lastError) {
+      logVerbose && console.log("extension not yet ready");
+      window.setTimeout(init, 200);
+      return;
+    }
+    initStyle();
+    setStyles(true);
+    registerHandlers();
+    // Hack to know when the extension is unloaded
+    let port = chrome.runtime.connect();
+
+    port.onDisconnect.addListener(() => {
+      logVerbose && console.log("chrome runtime disconnected, removing 
handlers");
+      setStyles(false);
+      for (let handler of handlers) {
+        document.removeEventListener(handler.type, handler.listener);
       }
     });
-  }
 
-  logVerbose && console.log("loading Taler content script");
-  init();
+    if (resp && resp.type == "pay") {
+      logVerbose && console.log("doing taler.pay with", resp.payDetail);
+      talerPay(resp.payDetail).then(handlePaymentResponse);
+      document.documentElement.style.visibility = "hidden";
+    }
+  });
+}
 
-  interface HandlerFn {
-    (detail: any, sendResponse: (msg: any) => void): void;
-  }
+interface HandlerFn {
+  (detail: any, sendResponse: (msg: any) => void): void;
+}
 
-  function generateNonce(): Promise<string> {
-    const walletMsg = {
-      type: "generate-nonce",
-    };
-    return new Promise<string>((resolve, reject) => {
-      chrome.runtime.sendMessage(walletMsg, (resp: any) => {
-        resolve(resp);
-      });
+function generateNonce(): Promise<string> {
+  const walletMsg = {
+    type: "generate-nonce",
+  };
+  return new Promise<string>((resolve, reject) => {
+    chrome.runtime.sendMessage(walletMsg, (resp: any) => {
+      resolve(resp);
     });
-  }
+  });
+}
 
-  function downloadContract(url: string, nonce: string): Promise<any> {
-    let parsed_url = new URI(url);
-    url = parsed_url.setQuery({nonce}).href();
-    // FIXME: include and check nonce!
-    return new Promise((resolve, reject) => {
-      const contract_request = new XMLHttpRequest();
-      console.log("downloading contract from '" + url + "'")
-      contract_request.open("GET", url, true);
-      contract_request.onload = function (e) {
-        if (contract_request.readyState == 4) {
-          if (contract_request.status == 200) {
-            console.log("response text:",
-                        contract_request.responseText);
-            var contract_wrapper = JSON.parse(contract_request.responseText);
-            if (!contract_wrapper) {
-              console.error("response text was invalid json");
-              let detail = {hint: "invalid json", status: 
contract_request.status, body: contract_request.responseText};
-              reject(detail);
-              return;
-            }
-            resolve(contract_wrapper);
-          } else {
-            let detail = {hint: "contract download failed", status: 
contract_request.status, body: contract_request.responseText};
+function downloadContract(url: string, nonce: string): Promise<any> {
+  let parsed_url = new URI(url);
+  url = parsed_url.setQuery({nonce}).href();
+  // FIXME: include and check nonce!
+  return new Promise((resolve, reject) => {
+    const contract_request = new XMLHttpRequest();
+    console.log("downloading contract from '" + url + "'")
+    contract_request.open("GET", url, true);
+    contract_request.onload = function (e) {
+      if (contract_request.readyState == 4) {
+        if (contract_request.status == 200) {
+          console.log("response text:",
+                      contract_request.responseText);
+          var contract_wrapper = JSON.parse(contract_request.responseText);
+          if (!contract_wrapper) {
+            console.error("response text was invalid json");
+            let detail = {hint: "invalid json", status: 
contract_request.status, body: contract_request.responseText};
             reject(detail);
             return;
           }
+          resolve(contract_wrapper);
+        } else {
+          let detail = {hint: "contract download failed", status: 
contract_request.status, body: contract_request.responseText};
+          reject(detail);
+          return;
         }
-      };
-      contract_request.onerror = function (e) {
-        let detail = {hint: "contract download failed", status: 
contract_request.status, body: contract_request.responseText};
-        reject(detail);
-        return;
-      };
-      contract_request.send();
-    });
+      }
+    };
+    contract_request.onerror = function (e) {
+      let detail = {hint: "contract download failed", status: 
contract_request.status, body: contract_request.responseText};
+      reject(detail);
+      return;
+    };
+    contract_request.send();
+  });
+}
+
+async function processProposal(proposal: any) {
+  if (!proposal.data) {
+    console.error("field proposal.data field missing");
+    return;
   }
 
-  async function processProposal(proposal: any) {
-    if (!proposal.data) {
-      console.error("field proposal.data field missing");
-      return;
+  if (!proposal.hash) {
+    console.error("proposal.hash field missing");
+    return;
+  }
+
+  let contractHash = await hashContract(proposal.data);
+
+  if (contractHash != proposal.hash) {
+    console.error("merchant-supplied contract hash is wrong");
+    return;
+  }
+
+  let merchantName = "(unknown)";
+  try {
+    merchantName = proposal.data.merchant.name;
+  } catch (e) {
+    // bad contract / name not included
+  }
+
+  let historyEntry = {
+    timestamp: (new Date).getTime(),
+    subjectId: `contract-${contractHash}`,
+    type: "offer-contract",
+    detail: {
+      contractHash,
+      merchantName,
     }
+  };
+  await putHistory(historyEntry);
+  let offerId = await saveOffer(proposal);
+
+  const uri = new URI(chrome.extension.getURL(
+    "/src/pages/confirm-contract.html"));
+  const params = {
+    offerId: offerId.toString(),
+  };
+  const target = uri.query(params).href();
+  document.location.replace(target);
+}
 
-    if (!proposal.hash) {
-      console.error("proposal.hash field missing");
+function talerPay(msg: any): Promise<any> {
+  return new Promise(async(resolve, reject) => {
+    // current URL without fragment
+    let url = new URI(document.location.href).fragment("").href();
+    let res = await queryPayment(url);
+    logVerbose && console.log("taler-pay: got response", res);
+    if (res && res.payReq) {
+      resolve(res);
       return;
     }
-
-    let contractHash = await hashContract(proposal.data);
-
-    if (contractHash != proposal.hash) {
-      console.error("merchant-supplied contract hash is wrong");
+    if (msg.contract_url) {
+      let nonce = await generateNonce();
+      let proposal = await downloadContract(msg.contract_url, nonce);
+      if (proposal.data.nonce != nonce) {
+        console.error("stale contract");
+        return;
+      }
+      await processProposal(proposal);
       return;
     }
 
-    let merchantName = "(unknown)";
-    try {
-      merchantName = proposal.data.merchant.name;
-    } catch (e) {
-      // bad contract / name not included
+    if (msg.offer_url) {
+      document.location.href = msg.offer_url;
+      return;
     }
 
-    let historyEntry = {
-      timestamp: (new Date).getTime(),
-      subjectId: `contract-${contractHash}`,
-      type: "offer-contract",
-      detail: {
-        contractHash,
-        merchantName,
-      }
-    };
-    await putHistory(historyEntry);
-    let offerId = await saveOffer(proposal);
+    console.log("can't proceed with payment, no way to get contract 
specified");
+  });
+}
 
-    const uri = new URI(chrome.extension.getURL(
-      "/src/pages/confirm-contract.html"));
-    const params = {
-      offerId: offerId.toString(),
+function talerPaymentFailed(H_contract: string) {
+  return new Promise(async(resolve, reject) => {
+    const walletMsg = {
+      type: "payment-failed",
+      detail: {
+        contractHash: H_contract
+      },
     };
-    const target = uri.query(params).href();
-    document.location.replace(target);
-  }
+    chrome.runtime.sendMessage(walletMsg, (resp) => {
+      resolve();
+    });
+  });
+}
 
-  function registerHandlers() {
-    /**
-     * Add a handler for a DOM event, which automatically
-     * handles adding sequence numbers to responses.
-     */
-    function addHandler(type: string, handler: HandlerFn) {
-      let handlerWrap = (e: CustomEvent) => {
-        if (e.type != type) {
-          throw Error(`invariant violated`);
-        }
-        let callId: number|undefined = undefined;
-        if (e.detail && e.detail.callId != undefined) {
-          callId = e.detail.callId;
-        }
-        let responder = (msg?: any) => {
-          let fullMsg = Object.assign({}, msg, {callId});
-          let opts = { detail: fullMsg };
-          if ("function" == typeof cloneInto) {
-            opts = cloneInto(opts, document.defaultView);
-          }
-          let evt = new CustomEvent(type + "-result", opts);
-          document.dispatchEvent(evt);
-        };
-        handler(e.detail, responder);
-      };
-      document.addEventListener(type, handlerWrap);
-      handlers.push({type, listener: handlerWrap});
+function talerPaymentSucceeded(msg: any) {
+  return new Promise((resolve, reject) => {
+    if (!msg.H_contract) {
+      console.error("H_contract missing in taler-payment-succeeded");
+      return;
     }
-
-
-    addHandler("taler-query-id", (msg: any, sendResponse: any) => {
-      // FIXME: maybe include this info in taoer-probe?
-      sendResponse({id: chrome.runtime.id})
+    if (!msg.merchantSig) {
+      console.error("merchantSig missing in taler-payment-succeeded");
+      return;
+    }
+    logVerbose && console.log("got taler-payment-succeeded");
+    const walletMsg = {
+      type: "payment-succeeded",
+      detail: {
+        merchantSig: msg.merchantSig,
+        contractHash: msg.H_contract,
+      },
+    };
+    chrome.runtime.sendMessage(walletMsg, (resp) => {
+      resolve();
     });
+  });
+}
 
-    addHandler("taler-probe", (msg: any, sendResponse: any) => {
-      sendResponse();
-    });
 
-    addHandler("taler-create-reserve", (msg: any) => {
-      let params = {
-        amount: JSON.stringify(msg.amount),
-        callback_url: new URI(msg.callback_url)
-          .absoluteTo(document.location.href),
-        bank_url: document.location.href,
-        wt_types: JSON.stringify(msg.wt_types),
-        suggested_exchange_url: msg.suggested_exchange_url,
+function registerHandlers() {
+  /**
+   * Add a handler for a DOM event, which automatically
+   * handles adding sequence numbers to responses.
+   */
+  function addHandler(type: string, handler: HandlerFn) {
+    let handlerWrap = (e: CustomEvent) => {
+      if (e.type != type) {
+        throw Error(`invariant violated`);
+      }
+      let callId: number|undefined = undefined;
+      if (e.detail && e.detail.callId != undefined) {
+        callId = e.detail.callId;
+      }
+      let responder = (msg?: any) => {
+        let fullMsg = Object.assign({}, msg, {callId});
+        let opts = { detail: fullMsg };
+        if ("function" == typeof cloneInto) {
+          opts = cloneInto(opts, document.defaultView);
+        }
+        let evt = new CustomEvent(type + "-result", opts);
+        document.dispatchEvent(evt);
       };
-      let uri = new 
URI(chrome.extension.getURL("/src/pages/confirm-create-reserve.html"));
-      let redirectUrl = uri.query(params).href();
-      window.location.href = redirectUrl;
-    });
+      handler(e.detail, responder);
+    };
+    document.addEventListener(type, handlerWrap);
+    handlers.push({type, listener: handlerWrap});
+  }
 
-    addHandler("taler-add-auditor", (msg: any) => {
-      let params = {
-        req: JSON.stringify(msg),
-      };
-      let uri = new 
URI(chrome.extension.getURL("/src/pages/add-auditor.html"));
-      let redirectUrl = uri.query(params).href();
-      window.location.href = redirectUrl;
-    });
 
-    addHandler("taler-confirm-reserve", (msg: any, sendResponse: any) => {
-      let walletMsg = {
-        type: "confirm-reserve",
-        detail: {
-          reservePub: msg.reserve_pub
-        }
-      };
-      chrome.runtime.sendMessage(walletMsg, (resp) => {
-        sendResponse();
-      });
-    });
+  addHandler("taler-query-id", (msg: any, sendResponse: any) => {
+    // FIXME: maybe include this info in taoer-probe?
+    sendResponse({id: chrome.runtime.id})
+  });
 
+  addHandler("taler-probe", (msg: any, sendResponse: any) => {
+    sendResponse();
+  });
 
-    addHandler("taler-confirm-contract", async(msg: any) => {
-      if (!msg.contract_wrapper) {
-        console.error("contract wrapper missing");
-        return;
+  addHandler("taler-create-reserve", (msg: any) => {
+    let params = {
+      amount: JSON.stringify(msg.amount),
+      callback_url: new URI(msg.callback_url)
+        .absoluteTo(document.location.href),
+      bank_url: document.location.href,
+      wt_types: JSON.stringify(msg.wt_types),
+      suggested_exchange_url: msg.suggested_exchange_url,
+    };
+    let uri = new 
URI(chrome.extension.getURL("/src/pages/confirm-create-reserve.html"));
+    let redirectUrl = uri.query(params).href();
+    window.location.href = redirectUrl;
+  });
+
+  addHandler("taler-add-auditor", (msg: any) => {
+    let params = {
+      req: JSON.stringify(msg),
+    };
+    let uri = new URI(chrome.extension.getURL("/src/pages/add-auditor.html"));
+    let redirectUrl = uri.query(params).href();
+    window.location.href = redirectUrl;
+  });
+
+  addHandler("taler-confirm-reserve", (msg: any, sendResponse: any) => {
+    let walletMsg = {
+      type: "confirm-reserve",
+      detail: {
+        reservePub: msg.reserve_pub
       }
+    };
+    chrome.runtime.sendMessage(walletMsg, (resp) => {
+      sendResponse();
+    });
+  });
 
-      const proposal = msg.contract_wrapper;
 
-      processProposal(proposal);
-    });
+  addHandler("taler-confirm-contract", async(msg: any) => {
+    if (!msg.contract_wrapper) {
+      console.error("contract wrapper missing");
+      return;
+    }
 
-    addHandler("taler-pay", async(msg: any, sendResponse: any) => {
-      // current URL without fragment
-      let url = new URI(document.location.href).fragment("").href();
-      let res = await queryPayment(url);
-      logVerbose && console.log("taler-pay: got response", res);
-      if (res && res.payReq) {
-        sendResponse(res);
-        return;
-      }
-      if (msg.contract_url) {
-        let nonce = await generateNonce();
-        let proposal = await downloadContract(msg.contract_url, nonce);
-        if (proposal.data.nonce != nonce) {
-          console.error("stale contract");
-          return;
-        }
-        await processProposal(proposal);
-        return;
-      }
+    const proposal = msg.contract_wrapper;
 
-      if (msg.offer_url) {
-        document.location.href = msg.offer_url;
-        return;
-      }
+    processProposal(proposal);
+  });
 
-      console.log("can't proceed with payment, no way to get contract 
specified");
-    });
+  addHandler("taler-pay", async(msg: any, sendResponse: any) => {
+    let resp = await talerPay(msg);
+    sendResponse(resp);
+  });
 
-    addHandler("taler-payment-failed", (msg: any, sendResponse: any) => {
-      const walletMsg = {
-        type: "payment-failed",
-        detail: {
-          contractHash: msg.H_contract
-        },
-      };
-      chrome.runtime.sendMessage(walletMsg, (resp) => {
-        sendResponse();
-      })
-    });
+  addHandler("taler-payment-failed", async(msg: any, sendResponse: any) => {
+    await talerPaymentFailed(msg.H_contract);
+    sendResponse();
+  });
 
-    addHandler("taler-payment-succeeded", (msg: any, sendResponse: any) => {
-      if (!msg.H_contract) {
-        console.error("H_contract missing in taler-payment-succeeded");
-        return;
-      }
-      logVerbose && console.log("got taler-payment-succeeded");
-      const walletMsg = {
-        type: "payment-succeeded",
-        detail: {
-          contractHash: msg.H_contract,
-        },
-      };
-      chrome.runtime.sendMessage(walletMsg, (resp) => {
-        sendResponse();
-      })
-    });
-  }
+  addHandler("taler-payment-succeeded", async(msg: any, sendResponse: any) => {
+    await talerPaymentSucceeded(msg);
+    sendResponse();
+  });
 }
+
+logVerbose && console.log("loading Taler content script");
+init();
+
diff --git a/src/cryptoApi.ts b/src/cryptoApi.ts
index 672b90d..98fc2c6 100644
--- a/src/cryptoApi.ts
+++ b/src/cryptoApi.ts
@@ -235,6 +235,10 @@ export class CryptoApi {
     return this.doRpc<boolean>("isValidDenom", 2, denom, masterPub);
   }
 
+  isValidPaymentSignature(sig: string, contractHash: string, merchantPub: 
string) {
+    return this.doRpc<PayCoinInfo>("isValidPaymentSignature", 1, sig, 
contractHash, merchantPub);
+  }
+
   signDeposit(offer: OfferRecord,
               cds: CoinWithDenom[]): Promise<PayCoinInfo> {
     return this.doRpc<PayCoinInfo>("signDeposit", 3, offer, cds);
diff --git a/src/cryptoWorker.ts b/src/cryptoWorker.ts
index 0abcb36..cb7bee4 100644
--- a/src/cryptoWorker.ts
+++ b/src/cryptoWorker.ts
@@ -97,6 +97,20 @@ namespace RpcFunctions {
   }
 
 
+  export function isValidPaymentSignature(sig: string, contractHash: string, 
merchantPub: string) {
+    let p = new native.PaymentSignaturePS({
+      contract_hash: native.HashCode.fromCrock(contractHash),
+    });
+    let nativeSig = new native.EddsaSignature();
+    nativeSig.loadCrock(sig);
+    let nativePub = native.EddsaPublicKey.fromCrock(merchantPub);
+    return native.eddsaVerify(native.SignaturePurpose.MERCHANT_PAYMENT_OK,
+                              p.toPurpose(),
+                              nativeSig,
+                              nativePub);
+  }
+
+
   export function isValidDenom(denom: DenominationRecord,
                                masterPub: string): boolean {
     let p = new native.DenominationKeyValidityPS({
diff --git a/src/emscriptif.ts b/src/emscriptif.ts
index 0b3f2ae..3a34f64 100644
--- a/src/emscriptif.ts
+++ b/src/emscriptif.ts
@@ -206,6 +206,7 @@ export enum SignaturePurpose {
   MASTER_DENOMINATION_KEY_VALIDITY = 1025,
   WALLET_COIN_MELT = 1202,
   TEST = 4242,
+  MERCHANT_PAYMENT_OK = 1104,
 }
 
 
@@ -1134,6 +1135,26 @@ export class DenominationKeyValidityPS extends 
SignatureStruct {
   }
 }
 
+export interface PaymentSignaturePS_args {
+  contract_hash: HashCode;
+}
+
+export class PaymentSignaturePS extends SignatureStruct {
+  constructor(w: PaymentSignaturePS_args) {
+    super(w);
+  }
+
+  purpose() {
+    return SignaturePurpose.MERCHANT_PAYMENT_OK;
+  }
+
+  fieldTypes() {
+    return [
+      ["contract_hash", HashCode],
+    ];
+  }
+}
+
 
 export class RsaPublicKey extends MallocArenaObject {
   static fromCrock(s: string): RsaPublicKey {
diff --git a/src/taler-wallet-lib.ts b/src/taler-wallet-lib.ts
deleted file mode 120000
index 20e5993..0000000
--- a/src/taler-wallet-lib.ts
+++ /dev/null
@@ -1 +0,0 @@
-../web-common/taler-wallet-lib.ts
\ No newline at end of file
diff --git a/src/wallet.ts b/src/wallet.ts
index a809b94..5104615 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -1787,7 +1787,7 @@ export class Wallet {
   }
 
 
-  async paymentSucceeded(contractHash: string): Promise<any> {
+  async paymentSucceeded(contractHash: string, merchantSig: string): 
Promise<any> {
     const doPaymentSucceeded = async() => {
       let t = await this.q().get<TransactionRecord>(Stores.transactions,
                                                     contractHash);
@@ -1795,6 +1795,13 @@ export class Wallet {
         console.error("contract not found");
         return;
       }
+      let merchantPub = t.contract.merchant_pub;
+      let valid = this.cryptoApi.isValidPaymentSignature(merchantSig, 
contractHash, merchantPub);
+      if (!valid) {
+        console.error("merchant payment signature invalid");
+        // FIXME: properly display error
+        return;
+      }
       t.finished = true;
       let modifiedCoins: CoinRecord[] = [];
       for (let pc of t.payReq.coins) {
diff --git a/src/wxBackend.ts b/src/wxBackend.ts
index 1aa10ce..984cad2 100644
--- a/src/wxBackend.ts
+++ b/src/wxBackend.ts
@@ -254,10 +254,14 @@ function makeHandlers(db: IDBDatabase,
     },
     ["payment-succeeded"]: function (detail, sender) {
       let contractHash = detail.contractHash;
+      let merchantSig = detail.merchantSig;
       if (!contractHash) {
         return Promise.reject(Error("contractHash missing"));
       }
-      return wallet.paymentSucceeded(contractHash);
+      if (!merchantSig) {
+        return Promise.reject(Error("merchantSig missing"));
+      }
+      return wallet.paymentSucceeded(contractHash, merchantSig);
     },
   };
 }
diff --git a/tsconfig.json b/tsconfig.json
index 93c7fb1..8dc8cb7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -48,7 +48,6 @@
     "src/pages/tree.tsx",
     "src/query.ts",
     "src/renderHtml.tsx",
-    "src/taler-wallet-lib.ts",
     "src/types-test.ts",
     "src/types.ts",
     "src/wallet-test.ts",
diff --git a/web-common b/web-common
index d7e0135..a8bff2e 160000
--- a/web-common
+++ b/web-common
@@ -1 +1 @@
-Subproject commit d7e013594d15388b1a7342a44a0e9c8d4ecca82d
+Subproject commit a8bff2e27b89feb3696cf0e3a49fc00155d92de5

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]