diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-02-22 21:52:53 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-02-22 21:53:05 +0100 |
commit | 2760591d4deb00b594010493a0bbd07b347204ab (patch) | |
tree | acce52e557f004b20391aee0771147f2b62f034d /extension | |
parent | 81428771b8fc526a692dd26cf3f1421b65e32d6d (diff) |
put all crypto into backend
Diffstat (limited to 'extension')
-rw-r--r-- | extension/lib/wallet/cryptoApi.ts | 19 | ||||
-rw-r--r-- | extension/lib/wallet/cryptoLib.ts | 91 | ||||
-rw-r--r-- | extension/lib/wallet/types.ts | 24 | ||||
-rw-r--r-- | extension/lib/wallet/wallet.ts | 197 |
4 files changed, 192 insertions, 139 deletions
diff --git a/extension/lib/wallet/cryptoApi.ts b/extension/lib/wallet/cryptoApi.ts index c29e9a45e..300b928db 100644 --- a/extension/lib/wallet/cryptoApi.ts +++ b/extension/lib/wallet/cryptoApi.ts @@ -18,6 +18,9 @@ import {PreCoin} from "./types"; import {Reserve} from "./types"; import {Denomination} from "./types"; +import {Offer} from "./wallet"; +import {CoinWithDenom} from "./wallet"; +import {PayCoinInfo} from "./types"; export class CryptoApi { private nextRpcId: number = 1; private rpcRegistry = {}; @@ -66,9 +69,25 @@ export class CryptoApi { return this.doRpc("createPreCoin", denom, reserve); } + hashRsaPub(rsaPub: string): Promise<string> { + return this.doRpc("hashRsaPub", rsaPub); + } isValidDenom(denom: Denomination, masterPub: string): Promise<boolean> { return this.doRpc("isValidDenom", denom, masterPub); } + + signDeposit(offer: Offer, + cds: CoinWithDenom[]): Promise<PayCoinInfo> { + return this.doRpc("signDeposit", offer, cds); + } + + createEddsaKeypair(): Promise<{priv: string, pub: string}> { + return this.doRpc("createEddsaKeypair"); + } + + rsaUnblind(sig: string, bk: string, pk: string): Promise<string> { + return this.doRpc("rsaUnblind", sig, bk, pk); + } }
\ No newline at end of file diff --git a/extension/lib/wallet/cryptoLib.ts b/extension/lib/wallet/cryptoLib.ts index 2c32b3a63..869ddbaff 100644 --- a/extension/lib/wallet/cryptoLib.ts +++ b/extension/lib/wallet/cryptoLib.ts @@ -23,8 +23,11 @@ import {Denomination} from "./types"; "use strict"; import * as native from "./emscriptif"; -import {PreCoin, Reserve} from "./types"; +import {PreCoin, Reserve, PayCoinInfo} from "./types"; import create = chrome.alarms.create; +import {Offer} from "./wallet"; +import {CoinWithDenom} from "./wallet"; +import {CoinPaySig} from "./types"; export function main(worker: Worker) { @@ -58,7 +61,8 @@ namespace RpcFunctions { * Create a pre-coin of the given denomination to be withdrawn from then given * reserve. */ - export function createPreCoin(denom: Denomination, reserve: Reserve): PreCoin { + export function createPreCoin(denom: Denomination, + reserve: Reserve): PreCoin { let reservePriv = new native.EddsaPrivateKey(); reservePriv.loadCrock(reserve.reserve_priv); let reservePub = new native.EddsaPublicKey(); @@ -107,7 +111,7 @@ namespace RpcFunctions { export function isValidDenom(denom: Denomination, - masterPub: string): boolean { + masterPub: string): boolean { let p = new native.DenominationKeyValidityPS({ master: native.EddsaPublicKey.fromCrock(masterPub), denom_hash: native.RsaPublicKey.fromCrock(denom.denom_pub) @@ -134,4 +138,85 @@ namespace RpcFunctions { nativePub); } + + + export function hashRsaPub(rsaPub: string): string { + return native.RsaPublicKey.fromCrock(rsaPub) + .encode() + .hash() + .toCrock(); + } + + + export function createEddsaKeypair(): {priv: string, pub: string} { + const priv = native.EddsaPrivateKey.create(); + const pub = priv.getPublicKey(); + return {priv: priv.toCrock(), pub: pub.toCrock()}; + } + + + export function rsaUnblind(sig, bk, pk): string { + let denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig), + native.RsaBlindingKey.fromCrock(bk), + native.RsaPublicKey.fromCrock(pk)); + return denomSig.encode().toCrock() + } + + + /** + * Generate updated coins (to store in the database) + * and deposit permissions for each given coin. + */ + export function signDeposit(offer: Offer, + cds: CoinWithDenom[]): PayCoinInfo { + let ret = []; + let amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency); + let amountRemaining = new native.Amount(offer.contract.amount); + for (let cd of cds) { + let coinSpend; + + if (amountRemaining.value == 0 && amountRemaining.fraction == 0) { + break; + } + + if (amountRemaining.cmp(new native.Amount(cd.coin.currentAmount)) < 0) { + coinSpend = new native.Amount(amountRemaining.toJson()); + } else { + coinSpend = new native.Amount(cd.coin.currentAmount); + } + + amountSpent.add(coinSpend); + amountRemaining.sub(coinSpend); + + let newAmount = new native.Amount(cd.coin.currentAmount); + newAmount.sub(coinSpend); + cd.coin.currentAmount = newAmount.toJson(); + + let d = new native.DepositRequestPS({ + h_contract: native.HashCode.fromCrock(offer.H_contract), + h_wire: native.HashCode.fromCrock(offer.contract.H_wire), + amount_with_fee: coinSpend.toNbo(), + coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub), + deposit_fee: new native.Amount(cd.denom.fee_deposit).toNbo(), + merchant: native.EddsaPublicKey.fromCrock(offer.contract.merchant_pub), + refund_deadline: native.AbsoluteTimeNbo.fromTalerString(offer.contract.refund_deadline), + timestamp: native.AbsoluteTimeNbo.fromTalerString(offer.contract.timestamp), + transaction_id: native.UInt64.fromNumber(offer.contract.transaction_id), + }); + + let coinSig = native.eddsaSign(d.toPurpose(), + native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv)) + .toCrock(); + + let s: CoinPaySig = { + coin_sig: coinSig, + coin_pub: cd.coin.coinPub, + ub_sig: cd.coin.denomSig, + denom_pub: cd.coin.denomPub, + f: coinSpend.toJson(), + }; + ret.push({sig: s, updatedCoin: cd.coin}); + } + return ret; + } } diff --git a/extension/lib/wallet/types.ts b/extension/lib/wallet/types.ts index cfd30bbc3..77603e083 100644 --- a/extension/lib/wallet/types.ts +++ b/extension/lib/wallet/types.ts @@ -120,12 +120,36 @@ export interface PreCoin { coinValue: AmountJson; } + export interface Reserve { mint_base_url: string reserve_priv: string; reserve_pub: string; } + +export interface CoinPaySig { + coin_sig: string; + coin_pub: string; + ub_sig: string; + denom_pub: string; + f: AmountJson; +} + + +export interface Coin { + coinPub: string; + coinPriv: string; + denomPub: string; + denomSig: string; + currentAmount: AmountJson; + mintBaseUrl: string; +} + + +export type PayCoinInfo = Array<{ updatedCoin: Coin, sig: CoinPaySig }>; + + export namespace Amounts { export interface Result { amount: AmountJson; diff --git a/extension/lib/wallet/wallet.ts b/extension/lib/wallet/wallet.ts index 2bd2beee5..8446d7194 100644 --- a/extension/lib/wallet/wallet.ts +++ b/extension/lib/wallet/wallet.ts @@ -27,10 +27,12 @@ import {HttpResponse, RequestException} from "./http"; import {Query} from "./query"; import {Checkable} from "./checkable"; import {canonicalizeBaseUrl} from "./helpers"; -import {ReserveCreationInfo} from "./types"; +import {ReserveCreationInfo, Amounts} from "./types"; import {PreCoin} from "./types"; import {Reserve} from "./types"; import {CryptoApi} from "./cryptoApi"; +import {Coin} from "./types"; +import {PayCoinInfo} from "./types"; "use strict"; @@ -68,16 +70,6 @@ export class KeysJson { } -export interface Coin { - coinPub: string; - coinPriv: string; - denomPub: string; - denomSig: string; - currentAmount: AmountJson; - mintBaseUrl: string; -} - - class MintInfo implements IMintInfo { baseUrl: string; masterPublicKey: string; @@ -147,14 +139,10 @@ class MintInfo implements IMintInfo { if (!valid) { throw Error("signature on denomination invalid"); } - - let d: Denomination = Object.assign({}, newDenom); - d.pub_hash = native.RsaPublicKey.fromCrock(d.denom_pub) - .encode() - .hash() - .toCrock(); - this.denoms.push(d); - + return cryptoApi.hashRsaPub(newDenom.denom_pub); + }) + .then((h) => { + this.denoms.push(Object.assign({}, newDenom, {pub_hash: h})); }); }); @@ -302,8 +290,6 @@ export interface Badge { setColor(c: string): void; } -type PayCoinInfo = Array<{ updatedCoin: Coin, sig: CoinPaySig }>; - function deepEquals(x, y) { if (x === y) { @@ -362,11 +348,8 @@ function copy(o) { * Rank two denomination by how desireable it is to withdraw them, * based on their fees and value. */ -function rankDenom(denom1: any, denom2: any) { - // Slow ... we should find a better way than to convert it evert time. - let v1 = new native.Amount(denom1.value); - let v2 = new native.Amount(denom2.value); - return (-1) * v1.cmp(v2); +function rankDenom(denom1: Denomination, denom2: Denomination) { + return (-1) * Amounts.cmp(denom1.value, denom2.value); } @@ -428,65 +411,6 @@ export class Wallet { /** - * Generate updated coins (to store in the database) - * and deposit permissions for each given coin. - */ - private static signDeposit(offer: Offer, - cds: CoinWithDenom[]): PayCoinInfo { - let ret = []; - let amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency); - let amountRemaining = new native.Amount(offer.contract.amount); - cds = copy(cds); - for (let cd of cds) { - let coinSpend; - - if (amountRemaining.value == 0 && amountRemaining.fraction == 0) { - break; - } - - if (amountRemaining.cmp(new native.Amount(cd.coin.currentAmount)) < 0) { - coinSpend = new native.Amount(amountRemaining.toJson()); - } else { - coinSpend = new native.Amount(cd.coin.currentAmount); - } - - amountSpent.add(coinSpend); - amountRemaining.sub(coinSpend); - - let newAmount = new native.Amount(cd.coin.currentAmount); - newAmount.sub(coinSpend); - cd.coin.currentAmount = newAmount.toJson(); - - let d = new native.DepositRequestPS({ - h_contract: native.HashCode.fromCrock(offer.H_contract), - h_wire: native.HashCode.fromCrock(offer.contract.H_wire), - amount_with_fee: coinSpend.toNbo(), - coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub), - deposit_fee: new native.Amount(cd.denom.fee_deposit).toNbo(), - merchant: native.EddsaPublicKey.fromCrock(offer.contract.merchant_pub), - refund_deadline: native.AbsoluteTimeNbo.fromTalerString(offer.contract.refund_deadline), - timestamp: native.AbsoluteTimeNbo.fromTalerString(offer.contract.timestamp), - transaction_id: native.UInt64.fromNumber(offer.contract.transaction_id), - }); - - let coinSig = native.eddsaSign(d.toPurpose(), - native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv)) - .toCrock(); - - let s: CoinPaySig = { - coin_sig: coinSig, - coin_pub: cd.coin.coinPub, - ub_sig: cd.coin.denomSig, - denom_pub: cd.coin.denomPub, - f: coinSpend.toJson(), - }; - ret.push({sig: s, updatedCoin: cd.coin}); - } - return ret; - } - - - /** * Get mints and associated coins that are still spendable, * but only if the sum the coins' remaining value exceeds the payment amount. */ @@ -647,9 +571,10 @@ export class Wallet { } console.log("about to record ..."); let mintUrl = Object.keys(mcs)[0]; - let ds = Wallet.signDeposit(offer, mcs[mintUrl]); - return this.recordConfirmPay(offer, ds, mintUrl) - .then((() => ({}))); + + return this.cryptoApi.signDeposit(offer, mcs[mintUrl]) + .then((ds) => this.recordConfirmPay(offer, ds, mintUrl)) + .then(() => ({})); }); } @@ -711,44 +636,43 @@ export class Wallet { * Create a reserve, but do not flag it as confirmed yet. */ createReserve(req: CreateReserveRequest): Promise<CreateReserveResponse> { - const reservePriv = native.EddsaPrivateKey.create(); - const reservePub = reservePriv.getPublicKey(); - - const now = (new Date).getTime(); - const canonMint = canonicalizeBaseUrl(req.mint); - - const reserveRecord = { - reserve_pub: reservePub.toCrock(), - reserve_priv: reservePriv.toCrock(), - mint_base_url: canonMint, - created: now, - last_query: null, - current_amount: null, - requested_amount: req.amount, - confirmed: false, - }; + return this.cryptoApi.createEddsaKeypair().then((keypair) => { + const now = (new Date).getTime(); + const canonMint = canonicalizeBaseUrl(req.mint); + + const reserveRecord = { + reserve_pub: keypair.pub, + reserve_priv: keypair.priv, + mint_base_url: canonMint, + created: now, + last_query: null, + current_amount: null, + requested_amount: req.amount, + confirmed: false, + }; - const historyEntry = { - type: "create-reserve", - timestamp: now, - detail: { - requestedAmount: req.amount, - reservePub: reserveRecord.reserve_pub, - } - }; + const historyEntry = { + type: "create-reserve", + timestamp: now, + detail: { + requestedAmount: req.amount, + reservePub: reserveRecord.reserve_pub, + } + }; - return Query(this.db) - .put("reserves", reserveRecord) - .put("history", historyEntry) - .finish() - .then(() => { - let r: CreateReserveResponse = { - mint: canonMint, - reservePub: reservePub.toCrock(), - }; - return r; - }); + return Query(this.db) + .put("reserves", reserveRecord) + .put("history", historyEntry) + .finish() + .then(() => { + let r: CreateReserveResponse = { + mint: canonMint, + reservePub: keypair.pub, + }; + return r; + }); + }); } @@ -806,18 +730,19 @@ export class Wallet { }); } let r = JSON.parse(resp.responseText); - let denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(r.ev_sig), - native.RsaBlindingKey.fromCrock(pc.blindingKey), - native.RsaPublicKey.fromCrock(pc.denomPub)); - let coin: Coin = { - coinPub: pc.coinPub, - coinPriv: pc.coinPriv, - denomPub: pc.denomPub, - denomSig: denomSig.encode().toCrock(), - currentAmount: pc.coinValue, - mintBaseUrl: pc.mintBaseUrl, - }; - return coin; + return this.cryptoApi.rsaUnblind(r.ev_sig, pc.blindingKey, pc.denomPub) + .then((denomSig) => { + let coin: Coin = { + coinPub: pc.coinPub, + coinPriv: pc.coinPriv, + denomPub: pc.denomPub, + denomSig: denomSig, + currentAmount: pc.coinValue, + mintBaseUrl: pc.mintBaseUrl, + }; + return coin; + + }); }); } @@ -965,7 +890,7 @@ export class Wallet { console.log("using old mint"); } - return mintInfo.mergeKeys(mintKeysJson, this) + return mintInfo.mergeKeys(mintKeysJson, this.cryptoApi) .then(() => { return Query(this.db) .put("mints", mintInfo) |