aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2020-11-03 17:39:30 +0100
committerFlorian Dold <florian@dold.me>2020-11-03 17:39:30 +0100
commitdffb293f2a3fa836ee007f19e36f873ed5d295e7 (patch)
treed2be687c45163c4f3fa597cf77d3aceecbd87178 /packages
parent0d37ec5e91f6704261070e01bb77d7c46f85b7a2 (diff)
check contract terms signature, handle errors
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-wallet-core/src/TalerErrorCode.ts7
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts14
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts13
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts38
-rw-r--r--packages/taler-wallet-core/src/types/dbTypes.ts4
5 files changed, 75 insertions, 1 deletions
diff --git a/packages/taler-wallet-core/src/TalerErrorCode.ts b/packages/taler-wallet-core/src/TalerErrorCode.ts
index bb97574ba..239dc2bd4 100644
--- a/packages/taler-wallet-core/src/TalerErrorCode.ts
+++ b/packages/taler-wallet-core/src/TalerErrorCode.ts
@@ -3308,6 +3308,13 @@ export enum TalerErrorCode {
WALLET_CONTRACT_TERMS_BASE_URL_MISMATCH = 7018,
/**
+ * The merchant's signature on the contract terms is invalid.
+ * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
+ * (A value of 0 indicates that the error is generated client-side).
+ */
+ WALLET_CONTRACT_TERMS_SIGNATURE_INVALID = 7019,
+
+ /**
* my comment
* Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR (500).
* (A value of 0 indicates that the error is generated client-side).
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index 44de6b674..286de5a17 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -398,6 +398,20 @@ export class CryptoApi {
);
}
+ isValidContractTermsSignature(
+ contractTermsHash: string,
+ sig: string,
+ merchantPub: string,
+ ): Promise<boolean> {
+ return this.doRpc<boolean>(
+ "isValidContractTermsSignature",
+ 4,
+ contractTermsHash,
+ sig,
+ merchantPub,
+ );
+ }
+
createRecoupRequest(coin: CoinRecord): Promise<RecoupRequest> {
return this.doRpc<RecoupRequest>("createRecoupRequest", 1, coin);
}
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index c39ebb44a..46ac7c8a6 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -80,6 +80,7 @@ enum SignaturePurpose {
WALLET_COIN_MELT = 1202,
TEST = 4242,
MERCHANT_PAYMENT_OK = 1104,
+ MERCHANT_CONTRACT = 1101,
WALLET_COIN_RECOUP = 1203,
WALLET_COIN_LINK = 1204,
EXCHANGE_CONFIRM_RECOUP = 1039,
@@ -297,6 +298,18 @@ export class CryptoImplementation {
return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
}
+ isValidContractTermsSignature(
+ contractTermsHash: string,
+ sig: string,
+ merchantPub: string,
+ ): boolean {
+ const cthDec = decodeCrock(contractTermsHash);
+ const p = buildSigPS(SignaturePurpose.MERCHANT_CONTRACT)
+ .put(cthDec)
+ .build();
+ return eddsaVerify(p, decodeCrock(sig), decodeCrock(merchantPub));
+ }
+
/**
* Create a new EdDSA key pair.
*/
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 0c45290fc..6079ea08f 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -58,6 +58,7 @@ import { Logger } from "../util/logging";
import { parsePayUri } from "../util/taleruri";
import {
guardOperationException,
+ makeErrorDetails,
OperationFailedAndReportedError,
OperationFailedError,
} from "./errors";
@@ -582,6 +583,19 @@ async function resetDownloadProposalRetry(
});
}
+async function failProposalPermanently(
+ ws: InternalWalletState,
+ proposalId: string,
+ err: TalerErrorDetails,
+): Promise<void> {
+ await ws.db.mutate(Stores.proposals, proposalId, (x) => {
+ x.retryInfo.active = false;
+ x.lastError = err;
+ x.proposalStatus = ProposalStatus.PERMANENTLY_FAILED;
+ return x;
+ });
+}
+
function getProposalRequestTimeout(proposal: ProposalRecord): Duration {
return durationMax(
{ d_ms: 60000 },
@@ -663,13 +677,33 @@ async function processDownloadProposalImpl(
const parsedContractTerms = codecForContractTerms().decode(
proposalResp.contract_terms,
);
+
+ const sigValid = await ws.cryptoApi.isValidContractTermsSignature(
+ contractTermsHash,
+ proposalResp.sig,
+ parsedContractTerms.merchant_pub,
+ );
+
+ if (!sigValid) {
+ const err = makeErrorDetails(
+ TalerErrorCode.WALLET_CONTRACT_TERMS_SIGNATURE_INVALID,
+ "merchant's signature on contract terms is invalid",
+ {
+ merchantPub: parsedContractTerms.merchant_pub,
+ orderId: parsedContractTerms.order_id,
+ },
+ );
+ await failProposalPermanently(ws, proposalId, err);
+ throw new OperationFailedAndReportedError(err);
+ }
+
const fulfillmentUrl = parsedContractTerms.fulfillment_url;
const baseUrlForDownload = proposal.merchantBaseUrl;
const baseUrlFromContractTerms = parsedContractTerms.merchant_base_url;
if (baseUrlForDownload !== baseUrlFromContractTerms) {
- throw OperationFailedAndReportedError.fromCode(
+ const err = makeErrorDetails(
TalerErrorCode.WALLET_CONTRACT_TERMS_BASE_URL_MISMATCH,
"merchant base URL mismatch",
{
@@ -677,6 +711,8 @@ async function processDownloadProposalImpl(
baseUrlFromContractTerms,
},
);
+ await failProposalPermanently(ws, proposalId, err);
+ throw new OperationFailedAndReportedError(err);
}
await ws.db.runWithWriteTransaction(
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts b/packages/taler-wallet-core/src/types/dbTypes.ts
index 4cd98cac1..8f6c22f22 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -814,6 +814,10 @@ export enum ProposalStatus {
*/
REFUSED = "refused",
/**
+ * Downloading or processing the proposal has failed permanently.
+ */
+ PERMANENTLY_FAILED = "permanently-failed",
+ /**
* Downloaded proposal was detected as a re-purchase.
*/
REPURCHASE = "repurchase",