From e42c282e67d49a54aa7aab2e87f9e458e488ae8e Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 5 Nov 2021 07:29:26 +0100 Subject: anastasis-core: make truth/policy payments compatible with C reducer --- packages/anastasis-core/src/crypto.ts | 3 +- packages/anastasis-core/src/index.ts | 84 +++++++++++++++++++++++----- packages/anastasis-core/src/reducer-types.ts | 13 +++++ 3 files changed, 85 insertions(+), 15 deletions(-) (limited to 'packages') diff --git a/packages/anastasis-core/src/crypto.ts b/packages/anastasis-core/src/crypto.ts index 9689e4f2d..6921cb2f1 100644 --- a/packages/anastasis-core/src/crypto.ts +++ b/packages/anastasis-core/src/crypto.ts @@ -11,8 +11,9 @@ import { stringToBytes, secretbox_open, hash, + Logger, + j2s, } from "@gnu-taler/taler-util"; -import { gzipSync } from "fflate"; import { argon2id } from "hash-wasm"; export type Flavor = T & { diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts index 0f391b733..8972b057c 100644 --- a/packages/anastasis-core/src/index.ts +++ b/packages/anastasis-core/src/index.ts @@ -16,6 +16,7 @@ import { HttpStatusCode, j2s, Logger, + parsePayUri, stringToBytes, TalerErrorCode, TalerSignaturePurpose, @@ -328,7 +329,6 @@ async function getTruthValue( * Compress the recovery document and add a size header. */ async function compressRecoveryDoc(rd: any): Promise { - logger.info(`recovery document: ${j2s(rd)}`); const docBytes = stringToBytes(JSON.stringify(rd)); const sizeHeaderBuf = new ArrayBuffer(4); const dvbuf = new DataView(sizeHeaderBuf); @@ -457,6 +457,8 @@ async function uploadSecret( const rd = recoveryData.recovery_document; const truthPayUris: string[] = []; + const truthPaySecrets: Record = {}; + const userIdCache: Record = {}; const getUserIdCaching = async (providerUrl: string) => { let userId = userIdCache[providerUrl]; @@ -483,9 +485,8 @@ async function uploadSecret( tm.truth_key, truthValue, ); - logger.info(`uploading to ${meth.provider}`); + logger.info(`uploading truth to ${meth.provider}`); const userId = await getUserIdCaching(meth.provider); - // FIXME: check that the question salt is okay here, looks weird. const encryptedKeyShare = await encryptKeyshare( tm.key_share, userId, @@ -500,10 +501,21 @@ async function uploadSecret( type: authMethod.type, truth_mime: authMethod.mime_type, }; - const resp = await fetch(new URL(`truth/${tm.uuid}`, meth.provider).href, { + const reqUrl = new URL(`truth/${tm.uuid}`, meth.provider); + const paySecret = (state.truth_upload_payment_secrets ?? {})[meth.provider]; + if (paySecret) { + // FIXME: Get this from the params + reqUrl.searchParams.set("timeout_ms", "500"); + } + const resp = await fetch(reqUrl.href, { method: "POST", headers: { "content-type": "application/json", + ...(paySecret + ? { + "Anastasis-Payment-Identifier": paySecret, + } + : {}), }, body: JSON.stringify(tur), }); @@ -520,6 +532,14 @@ async function uploadSecret( }; } truthPayUris.push(talerPayUri); + const parsedUri = parsePayUri(talerPayUri); + if (!parsedUri) { + return { + code: TalerErrorCode.ANASTASIS_REDUCER_BACKEND_FAILURE, + hint: `payment requested, but no taler://pay URI given`, + }; + } + truthPaySecrets[meth.provider] = parsedUri.orderId; continue; } return { @@ -532,6 +552,7 @@ async function uploadSecret( return { ...state, backup_state: BackupStates.TruthsPaying, + truth_upload_payment_secrets: truthPaySecrets, payments: truthPayUris, }; } @@ -539,6 +560,8 @@ async function uploadSecret( const successDetails: SuccessDetails = {}; const policyPayUris: string[] = []; + const policyPayUriMap: Record = {}; + //const policyPaySecrets: Record = {}; for (const prov of state.policy_providers!) { const userId = await getUserIdCaching(prov.provider_url); @@ -553,17 +576,33 @@ async function uploadSecret( .put(bodyHash) .build(); const sig = eddsaSign(sigPS, decodeCrock(acctKeypair.priv)); - const resp = await fetch( - new URL(`policy/${acctKeypair.pub}`, prov.provider_url).href, - { - method: "POST", - headers: { - "Anastasis-Policy-Signature": encodeCrock(sig), - "If-None-Match": encodeCrock(bodyHash), - }, - body: decodeCrock(encRecoveryDoc), + const talerPayUri = state.policy_payment_requests?.find( + (x) => x.provider === prov.provider_url, + )?.payto; + let paySecret: string | undefined; + if (talerPayUri) { + paySecret = parsePayUri(talerPayUri)!.orderId; + } + const reqUrl = new URL(`policy/${acctKeypair.pub}`, prov.provider_url); + if (paySecret) { + // FIXME: Get this from the params + reqUrl.searchParams.set("timeout_ms", "500"); + } + logger.info(`uploading policy to ${prov.provider_url}`); + const resp = await fetch(reqUrl.href, { + method: "POST", + headers: { + "Anastasis-Policy-Signature": encodeCrock(sig), + "If-None-Match": encodeCrock(bodyHash), + ...(paySecret + ? { + "Anastasis-Payment-Identifier": paySecret, + } + : {}), }, - ); + body: decodeCrock(encRecoveryDoc), + }); + logger.info(`got response for policy upload (http status ${resp.status})`); if (resp.status === HttpStatusCode.NoContent) { let policyVersion = 0; let policyExpiration: Timestamp = { t_ms: 0 }; @@ -592,6 +631,14 @@ async function uploadSecret( }; } policyPayUris.push(talerPayUri); + const parsedUri = parsePayUri(talerPayUri); + if (!parsedUri) { + return { + code: TalerErrorCode.ANASTASIS_REDUCER_BACKEND_FAILURE, + hint: `payment requested, but no taler://pay URI given`, + }; + } + policyPayUriMap[prov.provider_url] = talerPayUri; continue; } return { @@ -605,9 +652,17 @@ async function uploadSecret( ...state, backup_state: BackupStates.PoliciesPaying, payments: policyPayUris, + policy_payment_requests: Object.keys(policyPayUriMap).map((x) => { + return { + payto: policyPayUriMap[x], + provider: x, + }; + }), }; } + logger.info("backup finished"); + return { ...state, core_secret: undefined, @@ -766,6 +821,7 @@ async function requestTruth( const url = new URL(`/truth/${truth.uuid}`, truth.url); if (solveRequest) { + logger.info(`handling solve request ${j2s(solveRequest)}`); let respHash: string; switch (truth.escrow_type) { case ChallengeType.Question: { diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts index e5b0c47a4..1a560b885 100644 --- a/packages/anastasis-core/src/reducer-types.ts +++ b/packages/anastasis-core/src/reducer-types.ts @@ -87,10 +87,19 @@ export interface ReducerStateBackup { * Currently requested payments. * * List of taler://pay URIs. + * + * FIXME: There should be more information in this, + * including the provider and amount. */ payments?: string[]; + /** + * FIXME: Why is this not a map from provider to payto? + */ policy_payment_requests?: { + /** + * FIXME: This is not a payto URI, right?! + */ payto: string; provider: string; }[]; @@ -100,6 +109,10 @@ export interface ReducerStateBackup { expiration?: Timestamp; upload_fees?: { fee: AmountString }[]; + + // FIXME: The payment secrets and pay URIs should + // probably be consolidated into a single field. + truth_upload_payment_secrets?: Record; } export interface AuthMethod { -- cgit v1.2.3