summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-06-06 15:00:10 +0200
committerFlorian Dold <florian@dold.me>2023-06-06 15:00:15 +0200
commit474a171f5e6684ab7d11bb2987fc90fe6e1b37c8 (patch)
treefeb3184e1d8ad9a9edeea8a511fb4b9011a51d9a
parent9d35a7dc9b47701abf893987b5e0d3d1e99effb0 (diff)
downloadwallet-core-474a171f5e6684ab7d11bb2987fc90fe6e1b37c8.tar.gz
wallet-core-474a171f5e6684ab7d11bb2987fc90fe6e1b37c8.tar.bz2
wallet-core-474a171f5e6684ab7d11bb2987fc90fe6e1b37c8.zip
wallet-core: deterministic p2p contract encryption
-rw-r--r--packages/taler-util/src/taler-crypto.ts6
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoImplementation.ts2
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoTypes.ts3
-rw-r--r--packages/taler-wallet-core/src/db.ts13
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts116
6 files changed, 98 insertions, 46 deletions
diff --git a/packages/taler-util/src/taler-crypto.ts b/packages/taler-util/src/taler-crypto.ts
index c4eb925f7..ed3b82cd0 100644
--- a/packages/taler-util/src/taler-crypto.ts
+++ b/packages/taler-util/src/taler-crypto.ts
@@ -1465,6 +1465,7 @@ export function encryptContractForMerge(
contractPriv: ContractPrivateKey,
mergePriv: MergePrivateKey,
contractTerms: any,
+ nonce: EncryptionNonce,
): Promise<OpaqueData> {
const contractTermsCanon = canonicalJson(contractTerms) + "\0";
const contractTermsBytes = stringToBytes(contractTermsCanon);
@@ -1476,13 +1477,14 @@ export function encryptContractForMerge(
contractTermsCompressed,
]);
const key = keyExchangeEcdhEddsa(contractPriv, pursePub);
- return encryptWithDerivedKey(getRandomBytesF(24), key, data, mergeSalt);
+ return encryptWithDerivedKey(nonce, key, data, mergeSalt);
}
export function encryptContractForDeposit(
pursePub: PursePublicKey,
contractPriv: ContractPrivateKey,
contractTerms: any,
+ nonce: EncryptionNonce,
): Promise<OpaqueData> {
const contractTermsCanon = canonicalJson(contractTerms) + "\0";
const contractTermsBytes = stringToBytes(contractTermsCanon);
@@ -1493,7 +1495,7 @@ export function encryptContractForDeposit(
contractTermsCompressed,
]);
const key = keyExchangeEcdhEddsa(contractPriv, pursePub);
- return encryptWithDerivedKey(getRandomBytesF(24), key, data, depositSalt);
+ return encryptWithDerivedKey(nonce, key, data, depositSalt);
}
export interface DecryptForMergeResult {
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index c0c8e0d01..15ca1ea95 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -1495,6 +1495,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
decodeCrock(req.contractPriv),
decodeCrock(req.mergePriv),
req.contractTerms,
+ decodeCrock(req.nonce),
);
const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_PURSE_ECONTRACT)
.put(hash(enc))
@@ -1531,6 +1532,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
decodeCrock(req.pursePub),
decodeCrock(req.contractPriv),
req.contractTerms,
+ decodeCrock(req.nonce),
);
const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_PURSE_ECONTRACT)
.put(hash(enc))
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 930db03a8..7eaa4a55a 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -181,6 +181,7 @@ export interface EncryptContractRequest {
pursePub: string;
pursePriv: string;
mergePriv: string;
+ nonce: string;
}
export interface EncryptContractResponse {
@@ -195,6 +196,8 @@ export interface EncryptContractForDepositRequest {
pursePub: string;
pursePriv: string;
+
+ nonce: string;
}
export interface EncryptContractForDepositResponse {
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index a5db49649..c4cdf769b 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1839,6 +1839,14 @@ export interface PeerPushPaymentInitiationRecord {
contractPriv: string;
contractPub: string;
+ /**
+ * 24 byte nonce.
+ */
+ contractEncNonce: string;
+
+ /**
+ * FIXME: Put those in a different object store!
+ */
contractTerms: PeerContractTerms;
purseExpiration: TalerProtocolTimestamp;
@@ -1911,6 +1919,11 @@ export interface PeerPullPaymentInitiationRecord {
contractPub: string;
contractPriv: string;
+ contractEncNonce: string;
+
+ /**
+ * FIXME: Put in separate object store!
+ */
contractTerms: PeerContractTerms;
mergeTimestamp: TalerPreciseTimestamp;
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
index a85df66d2..fc2718ce5 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
@@ -405,6 +405,7 @@ export async function processPeerPullCredit(
contractTerms: pullIni.contractTerms,
pursePriv: pullIni.pursePriv,
pursePub: pullIni.pursePub,
+ nonce: pullIni.contractEncNonce,
});
const purseExpiration = pullIni.contractTerms.purse_expiration;
@@ -690,6 +691,8 @@ export async function initiatePeerPullPayment(
const mergeReserveRowId = mergeReserveInfo.rowId;
checkDbInvariant(!!mergeReserveRowId);
+ const contractEncNonce = encodeCrock(getRandomBytes(24));
+
const wi = await getExchangeWithdrawalInfo(
ws,
exchangeBaseUrl,
@@ -711,6 +714,7 @@ export async function initiatePeerPullPayment(
status: PeerPullPaymentInitiationStatus.PendingCreatePurse,
contractTerms: contractTerms,
mergeTimestamp,
+ contractEncNonce,
mergeReserveRowId: mergeReserveRowId,
contractPriv: contractKeyPair.priv,
contractPub: contractKeyPair.pub,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
index ac0aa9c87..a03980d46 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
@@ -34,6 +34,10 @@ import {
TransactionMinorState,
TransactionState,
TransactionType,
+ decodeCrock,
+ encodeCrock,
+ getRandomBytes,
+ hash,
j2s,
stringifyTalerUri,
} from "@gnu-taler/taler-util";
@@ -57,11 +61,7 @@ import {
OperationAttemptResultType,
constructTaskIdentifier,
} from "../util/retries.js";
-import {
- runLongpollAsync,
- spendCoins,
- runOperationWithErrorReporting,
-} from "./common.js";
+import { runLongpollAsync, spendCoins } from "./common.js";
import {
constructTransactionIdentifier,
notifyTransition,
@@ -69,6 +69,7 @@ import {
} from "./transactions.js";
import { assertUnreachable } from "../util/assertUnreachable.js";
import { checkLogicInvariant } from "../util/invariants.js";
+import { EncryptContractRequest } from "../crypto/cryptoTypes.js";
const logger = new Logger("pay-peer-push-debit.ts");
@@ -100,6 +101,7 @@ async function processPeerPushDebitCreateReserve(
ws: InternalWalletState,
peerPushInitiation: PeerPushPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
+ logger.info("processing peer-push-debit pending(create-reserve)");
const pursePub = peerPushInitiation.pursePub;
const purseExpiration = peerPushInitiation.purseExpiration;
const hContractTerms = peerPushInitiation.contractTermsHash;
@@ -124,32 +126,49 @@ async function processPeerPushDebitCreateReserve(
coins,
});
- const econtractResp = await ws.cryptoApi.encryptContractForMerge({
+ const encryptContractRequest: EncryptContractRequest = {
contractTerms: peerPushInitiation.contractTerms,
mergePriv: peerPushInitiation.mergePriv,
pursePriv: peerPushInitiation.pursePriv,
pursePub: peerPushInitiation.pursePub,
contractPriv: peerPushInitiation.contractPriv,
contractPub: peerPushInitiation.contractPub,
- });
+ nonce: peerPushInitiation.contractEncNonce,
+ };
+
+ logger.info(`encrypt contract request: ${j2s(encryptContractRequest)}`);
+
+ const econtractResp = await ws.cryptoApi.encryptContractForMerge(
+ encryptContractRequest,
+ );
+
+ const econtractHash = encodeCrock(
+ hash(decodeCrock(econtractResp.econtract.econtract)),
+ );
+
+ logger.info(`econtract hash: ${econtractHash}`);
const createPurseUrl = new URL(
`purses/${peerPushInitiation.pursePub}/create`,
peerPushInitiation.exchangeBaseUrl,
);
+ const reqBody = {
+ amount: peerPushInitiation.amount,
+ merge_pub: peerPushInitiation.mergePub,
+ purse_sig: purseSigResp.sig,
+ h_contract_terms: hContractTerms,
+ purse_expiration: purseExpiration,
+ deposits: depositSigsResp.deposits,
+ min_age: 0,
+ econtract: econtractResp.econtract,
+ };
+
+ logger.info(`request body: ${j2s(reqBody)}`);
+
const httpResp = await ws.http.fetch(createPurseUrl.href, {
method: "POST",
- body: {
- amount: peerPushInitiation.amount,
- merge_pub: peerPushInitiation.mergePub,
- purse_sig: purseSigResp.sig,
- h_contract_terms: hContractTerms,
- purse_expiration: purseExpiration,
- deposits: depositSigsResp.deposits,
- min_age: 0,
- econtract: econtractResp.econtract,
- },
+ body: reqBody,
});
const resp = await httpResp.json();
@@ -157,24 +176,16 @@ async function processPeerPushDebitCreateReserve(
logger.info(`resp: ${j2s(resp)}`);
if (httpResp.status !== HttpStatusCode.Ok) {
+ // FIXME: do proper error reporting
throw Error("got error response from exchange");
}
- await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
- .runReadWrite(async (tx) => {
- const ppi = await tx.peerPushPaymentInitiations.get(pursePub);
- if (!ppi) {
- return;
- }
- ppi.status = PeerPushPaymentInitiationStatus.Done;
- await tx.peerPushPaymentInitiations.put(ppi);
- });
+ await transitionPeerPushDebitTransaction(ws, pursePub, {
+ stFrom: PeerPushPaymentInitiationStatus.PendingCreatePurse,
+ stTo: PeerPushPaymentInitiationStatus.PendingReady,
+ });
- return {
- type: OperationAttemptResultType.Finished,
- result: undefined,
- };
+ return OperationAttemptResult.finishedEmpty();
}
async function processPeerPushDebitAbortingDeletePurse(
@@ -278,6 +289,7 @@ async function transitionPeerPushDebitTransaction(
const oldTxState = computePeerPushDebitTransactionState(ppiRec);
ppiRec.status = transitionSpec.stTo;
const newTxState = computePeerPushDebitTransactionState(ppiRec);
+ // FIXME: We don't transition here?!
return {
oldTxState,
newTxState,
@@ -341,6 +353,7 @@ async function processPeerPushDebitReady(
ws: InternalWalletState,
peerPushInitiation: PeerPushPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
+ logger.info("processing peer-push-debit pending(ready)");
const pursePub = peerPushInitiation.pursePub;
const retryTag = constructTaskIdentifier({
tag: PendingTaskType.PeerPushDebit,
@@ -434,6 +447,12 @@ export async function processPeerPushDebit(
return processPeerPushDebitAbortingDeletePurse(ws, peerPushInitiation);
case PeerPushPaymentInitiationStatus.AbortingRefresh:
return processPeerPushDebitAbortingRefresh(ws, peerPushInitiation);
+ default: {
+ const txState = computePeerPushDebitTransactionState(peerPushInitiation);
+ logger.warn(
+ `not processing peer-push-debit transaction in state ${j2s(txState)}`,
+ );
+ }
}
return {
@@ -482,7 +501,16 @@ export async function initiatePeerPushDebit(
coinSelRes.result.coins,
);
- await ws.db
+ const pursePub = pursePair.pub;
+
+ const transactionId = constructTaskIdentifier({
+ tag: PendingTaskType.PeerPushDebit,
+ pursePub,
+ });
+
+ const contractEncNonce = encodeCrock(getRandomBytes(24));
+
+ const transitionInfo = await ws.db
.mktx((x) => [
x.exchanges,
x.contractTerms,
@@ -509,7 +537,7 @@ export async function initiatePeerPushDebit(
refreshReason: RefreshReason.PayPeerPush,
});
- await tx.peerPushPaymentInitiations.add({
+ const ppi: PeerPushPaymentInitiationRecord = {
amount: Amounts.stringify(instructedAmount),
contractPriv: contractKeyPair.priv,
contractPub: contractKeyPair.pub,
@@ -523,27 +551,28 @@ export async function initiatePeerPushDebit(
timestampCreated: TalerPreciseTimestamp.now(),
status: PeerPushPaymentInitiationStatus.PendingCreatePurse,
contractTerms: contractTerms,
+ contractEncNonce,
coinSel: {
coinPubs: sel.coins.map((x) => x.coinPub),
contributions: sel.coins.map((x) => x.contribution),
},
totalCost: Amounts.stringify(totalAmount),
- });
+ };
+
+ await tx.peerPushPaymentInitiations.add(ppi);
await tx.contractTerms.put({
h: hContractTerms,
contractTermsRaw: contractTerms,
});
- });
- const taskId = constructTaskIdentifier({
- tag: PendingTaskType.PeerPushDebit,
- pursePub: pursePair.pub,
- });
-
- await runOperationWithErrorReporting(ws, taskId, async () => {
- return await processPeerPushDebit(ws, pursePair.pub);
- });
+ const newTxState = computePeerPushDebitTransactionState(ppi);
+ return {
+ oldTxState: { major: TransactionMajorState.None },
+ newTxState,
+ };
+ });
+ notifyTransition(ws, transactionId, transitionInfo);
return {
contractPriv: contractKeyPair.priv,
@@ -903,4 +932,3 @@ export function computePeerPushDebitTransactionState(
};
}
}
-