diff --git a/src/content_scripts/notify.ts b/src/content_scripts/notify.ts index 6a0d9d298..582375e1d 100644 --- a/src/content_scripts/notify.ts +++ b/src/content_scripts/notify.ts @@ -173,6 +173,17 @@ namespace TalerNotify { (detail: any, sendResponse: (msg: any) => void): void; } + function generateNonce(): Promise { + const walletMsg = { + type: "generate-nonce", + }; + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage(walletMsg, (resp: any) => { + resolve(resp); + }); + }); + } + function downloadContract(url: string, nonce: string): Promise { let parsed_url = URI(url); url = parsed_url.setQuery({nonce}).href(); @@ -358,7 +369,7 @@ namespace TalerNotify { return; } if (msg.contract_url) { - let nonce = Math.round(Math.random() * 0xFFFF).toString() + let nonce = await generateNonce(); let proposal = await downloadContract(msg.contract_url, nonce); if (proposal.data.nonce != nonce) { console.error("stale contract"); diff --git a/src/wallet.ts b/src/wallet.ts index 1c9de0170..67393edae 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -198,6 +198,11 @@ export interface Badge { stopBusy(): void; } +export interface NonceRecord { + priv: string; + pub: string; +} + function setTimeout(f: any, t: number) { return chrome.extension.getBackgroundPage().setTimeout(f, t); @@ -305,6 +310,12 @@ export namespace Stores { pubKeyIndex = new Index(this, "pubKey", "masterPublicKey"); } + class NonceStore extends Store { + constructor() { + super("nonces", {keyPath: "pub"}); + } + } + class CoinsStore extends Store { constructor() { super("coins", {keyPath: "coinPub"}); @@ -358,6 +369,7 @@ export namespace Stores { } export const exchanges: ExchangeStore = new ExchangeStore(); + export const nonces: NonceStore = new NonceStore(); export const transactions: TransactionsStore = new TransactionsStore(); export const reserves: Store = new Store("reserves", {keyPath: "reserve_pub"}); export const coins: CoinsStore = new CoinsStore(); @@ -1708,6 +1720,19 @@ export class Wallet { } + /** + * Generate a nonce in form of an EdDSA public key. + * Store the private key in our DB, so we can prove ownership. + */ + async generateNonce(): Promise { + let {priv, pub} = await this.cryptoApi.createEddsaKeypair(); + await this.q() + .put(Stores.nonces, {priv, pub}) + .finish(); + return pub; + } + + async paymentSucceeded(contractHash: string): Promise { const doPaymentSucceeded = async() => { let t = await this.q().get(Stores.transactions, diff --git a/src/wxBackend.ts b/src/wxBackend.ts index 50e068946..cdc8f4392 100644 --- a/src/wxBackend.ts +++ b/src/wxBackend.ts @@ -35,7 +35,7 @@ import * as logging from "./logging"; "use strict"; const DB_NAME = "taler"; -const DB_VERSION = 12; +const DB_VERSION = 14; import {Stores} from "./wallet"; import {Store, Index} from "./query"; @@ -102,6 +102,9 @@ function makeHandlers(db: IDBDatabase, const req = ConfirmReserveRequest.checked(d); return wallet.confirmReserve(req); }, + ["generate-nonce"]: function (detail, sender) { + return wallet.generateNonce(); + }, ["confirm-pay"]: function (detail, sender) { let offer: OfferRecord; try {