anastasis-core: make truth/policy payments compatible with C reducer

This commit is contained in:
Florian Dold 2021-11-05 07:29:26 +01:00
parent 34d2e4703d
commit e42c282e67
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 85 additions and 15 deletions

View File

@ -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, FlavorT extends string> = T & {

View File

@ -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<Uint8Array> {
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<string, string> = {};
const userIdCache: Record<string, UserIdentifier> = {};
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<string, string> = {};
//const policyPaySecrets: Record<string, string> = {};
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: {

View File

@ -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<string, string>;
}
export interface AuthMethod {