diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-12-05 23:07:46 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-12-05 23:07:46 +0100 |
commit | 7b54439fd62bd2a5e15b3068a8fbaffeb0a57468 (patch) | |
tree | 09b32371728d4a0e40f1fb24b415b6dc1f488962 | |
parent | 8115ac660cd9d12ef69ca80fc2e4cf8eec6b1ba1 (diff) |
validate wire fees and acct info
-rw-r--r-- | src/crypto/workers/cryptoApi.ts | 4 | ||||
-rw-r--r-- | src/crypto/workers/cryptoImplementation.ts | 26 | ||||
-rw-r--r-- | src/wallet-impl/exchanges.ts | 39 |
3 files changed, 59 insertions, 10 deletions
diff --git a/src/crypto/workers/cryptoApi.ts b/src/crypto/workers/cryptoApi.ts index 5537bb39f..aa1ff2c42 100644 --- a/src/crypto/workers/cryptoApi.ts +++ b/src/crypto/workers/cryptoApi.ts @@ -409,6 +409,10 @@ export class CryptoApi { return this.doRpc<boolean>("rsaVerify", 4, hm, sig, pk); } + isValidWireAccount(paytoUri: string, sig: string, masterPub: string): Promise<boolean> { + return this.doRpc<boolean>("isValidWireAccount", 4, paytoUri, sig, masterPub); + } + createPaybackRequest(coin: CoinRecord): Promise<PaybackRequest> { return this.doRpc<PaybackRequest>("createPaybackRequest", 1, coin); } diff --git a/src/crypto/workers/cryptoImplementation.ts b/src/crypto/workers/cryptoImplementation.ts index 00d81ce27..fa5a30d68 100644 --- a/src/crypto/workers/cryptoImplementation.ts +++ b/src/crypto/workers/cryptoImplementation.ts @@ -68,15 +68,17 @@ import { rsaVerify, } from "../talerCrypto"; import { randomBytes } from "../primitives/nacl-fast"; +import { kdf } from "../primitives/kdf"; enum SignaturePurpose { RESERVE_WITHDRAW = 1200, WALLET_COIN_DEPOSIT = 1201, MASTER_DENOMINATION_KEY_VALIDITY = 1025, + MASTER_WIRE_FEES = 1028, + MASTER_WIRE_DETAILS = 1030, WALLET_COIN_MELT = 1202, TEST = 4242, MERCHANT_PAYMENT_OK = 1104, - MASTER_WIRE_FEES = 1028, WALLET_COIN_PAYBACK = 1203, WALLET_COIN_LINK = 1204, } @@ -157,9 +159,7 @@ export class CryptoImplementation { * Create a pre-coin of the given denomination to be withdrawn from then given * reserve. */ - createPlanchet( - req: PlanchetCreationRequest, - ): PlanchetCreationResult { + createPlanchet(req: PlanchetCreationRequest): PlanchetCreationResult { const reservePub = decodeCrock(req.reservePub); const reservePriv = decodeCrock(req.reservePriv); const denomPub = decodeCrock(req.denomPub); @@ -264,6 +264,7 @@ export class CryptoImplementation { .put(timestampToBuffer(wf.startStamp)) .put(timestampToBuffer(wf.endStamp)) .put(amountToBuffer(wf.wireFee)) + .put(amountToBuffer(wf.closingFee)) .build(); const sig = decodeCrock(wf.sig); const pub = decodeCrock(masterPub); @@ -292,6 +293,23 @@ export class CryptoImplementation { return eddsaVerify(p, sig, pub); } + isValidWireAccount( + paytoUri: string, + sig: string, + masterPub: string, + ): boolean { + const h = kdf( + 64, + stringToBytes("exchange-wire-signature"), + stringToBytes(paytoUri + "\0"), + new Uint8Array(0), + ); + const p = buildSigPS(SignaturePurpose.MASTER_WIRE_DETAILS) + .put(h) + .build(); + return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub)); + } + /** * Create a new EdDSA key pair. */ diff --git a/src/wallet-impl/exchanges.ts b/src/wallet-impl/exchanges.ts index b89f3f84e..b6a2f1c8a 100644 --- a/src/wallet-impl/exchanges.ts +++ b/src/wallet-impl/exchanges.ts @@ -15,10 +15,13 @@ */ import { InternalWalletState } from "./state"; +import { WALLET_CACHE_BREAKER_CLIENT_VERSION } from "../wallet"; import { - WALLET_CACHE_BREAKER_CLIENT_VERSION, -} from "../wallet"; -import { KeysJson, Denomination, ExchangeWireJson } from "../talerTypes"; + KeysJson, + Denomination, + ExchangeWireJson, + WireFeesJson, +} from "../talerTypes"; import { getTimestampNow, OperationError } from "../walletTypes"; import { ExchangeRecord, @@ -229,8 +232,12 @@ async function updateExchangeWithWireInfo( if (exchange.updateStatus != ExchangeUpdateStatus.FETCH_WIRE) { return; } + const details = exchange.details; + if (!details) { + throw Error("invalid exchange state"); + } const reqUrl = new URL("wire", exchangeBaseUrl); - reqUrl.searchParams.set("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION) + reqUrl.searchParams.set("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION); const resp = await ws.http.get(reqUrl.href); @@ -239,6 +246,17 @@ async function updateExchangeWithWireInfo( throw Error("/wire response malformed"); } const wireInfo = ExchangeWireJson.checked(wiJson); + for (const a of wireInfo.accounts) { + console.log("validating exchange acct"); + const isValid = await ws.cryptoApi.isValidWireAccount( + a.url, + a.master_sig, + details.masterPublicKey, + ); + if (!isValid) { + throw Error("exchange acct signature invalid"); + } + } const feesForType: { [wireMethod: string]: WireFee[] } = {}; for (const wireMethod of Object.keys(wireInfo.fees)) { const feeList: WireFee[] = []; @@ -251,13 +269,22 @@ async function updateExchangeWithWireInfo( if (!endStamp) { throw Error("wrong date format"); } - feeList.push({ + const fee: WireFee = { closingFee: Amounts.parseOrThrow(x.closing_fee), endStamp, sig: x.sig, startStamp, wireFee: Amounts.parseOrThrow(x.wire_fee), - }); + }; + const isValid = await ws.cryptoApi.isValidWireFee( + wireMethod, + fee, + details.masterPublicKey, + ); + if (!isValid) { + throw Error("exchange wire fee signature invalid"); + } + feeList.push(fee); } feesForType[wireMethod] = feeList; } |