From 04356cd23fef76d2020338d2b2b394095fdc2b14 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 3 Nov 2021 13:34:57 +0100 Subject: anastasis: refactor feedback types --- packages/anastasis-core/src/index.ts | 150 ++++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 66 deletions(-) (limited to 'packages/anastasis-core/src/index.ts') diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts index db99db610..859dd083b 100644 --- a/packages/anastasis-core/src/index.ts +++ b/packages/anastasis-core/src/index.ts @@ -11,10 +11,9 @@ import { Duration, eddsaSign, encodeCrock, - getDurationRemaining, getRandomBytes, - getTimestampNow, hash, + HttpStatusCode, j2s, Logger, stringToBytes, @@ -91,6 +90,7 @@ import { import { unzlibSync, zlibSync } from "fflate"; import { EscrowMethod, RecoveryDocument } from "./recovery-document-types.js"; import { ProviderInfo, suggestPolicies } from "./policy-suggestion.js"; +import { ChallengeFeedback, ChallengeFeedbackStatus } from "./challenge-feedback-types.js"; const { fetch } = fetchPonyfill({}); @@ -291,7 +291,6 @@ async function backupEnterUserAttributes( return newState; } - /** * Truth data as stored in the reducer. */ @@ -551,6 +550,7 @@ async function uploadSecret( return { ...state, + core_secret: undefined, backup_state: BackupStates.BackupFinished, success_details: successDetails, }; @@ -684,25 +684,24 @@ async function tryRecoverSecret( return { ...state }; } -async function solveChallenge( +/** + * Request a truth, optionally with a challenge solution + * provided by the user. + */ +async function requestTruth( state: ReducerStateRecovery, - ta: ActionArgsSolveChallengeRequest, + truth: EscrowMethod, + solveRequest?: ActionArgsSolveChallengeRequest, ): Promise { - const recDoc: RecoveryDocument = state.verbatim_recovery_document!; - const truth = recDoc.escrow_methods.find( - (x) => x.uuid === state.selected_challenge_uuid, - ); - if (!truth) { - throw "truth for challenge not found"; - } - const url = new URL(`/truth/${truth.uuid}`, truth.url); - // FIXME: This isn't correct for non-question truth responses. - url.searchParams.set( - "response", - await secureAnswerHash(ta.answer, truth.uuid, truth.truth_salt), - ); + if (solveRequest) { + // FIXME: This isn't correct for non-question truth responses. + url.searchParams.set( + "response", + await secureAnswerHash(solveRequest.answer, truth.uuid, truth.truth_salt), + ); + } const resp = await fetch(url.href, { headers: { @@ -710,48 +709,79 @@ async function solveChallenge( }, }); - if (resp.status !== 200) { - return { - code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED, - hint: "got non-200 response", - http_status: resp.status, - } as ReducerStateError; - } + if (resp.status === HttpStatusCode.Ok) { + const answerSalt = + solveRequest && truth.escrow_type === "question" + ? solveRequest.answer + : undefined; - const answerSalt = truth.escrow_type === "question" ? ta.answer : undefined; + const userId = await userIdentifierDerive( + state.identity_attributes, + truth.provider_salt, + ); - const userId = await userIdentifierDerive( - state.identity_attributes, - truth.provider_salt, - ); + const respBody = new Uint8Array(await resp.arrayBuffer()); + const keyShare = await decryptKeyShare( + encodeCrock(respBody), + userId, + answerSalt, + ); - const respBody = new Uint8Array(await resp.arrayBuffer()); - const keyShare = await decryptKeyShare( - encodeCrock(respBody), - userId, - answerSalt, - ); + const recoveredKeyShares = { + ...(state.recovered_key_shares ?? {}), + [truth.uuid]: keyShare, + }; - const recoveredKeyShares = { - ...(state.recovered_key_shares ?? {}), - [truth.uuid]: keyShare, - }; + const challengeFeedback: { [x: string]: ChallengeFeedback } = { + ...state.challenge_feedback, + [truth.uuid]: { + state: ChallengeFeedbackStatus.Solved, + }, + }; - const challengeFeedback = { - ...state.challenge_feedback, - [truth.uuid]: { - state: "solved", - }, - }; + const newState: ReducerStateRecovery = { + ...state, + recovery_state: RecoveryStates.ChallengeSelecting, + challenge_feedback: challengeFeedback, + recovered_key_shares: recoveredKeyShares, + }; - const newState: ReducerStateRecovery = { - ...state, - recovery_state: RecoveryStates.ChallengeSelecting, - challenge_feedback: challengeFeedback, - recovered_key_shares: recoveredKeyShares, - }; + return tryRecoverSecret(newState); + } + + if (resp.status === HttpStatusCode.Forbidden) { + return { + ...state, + recovery_state: RecoveryStates.ChallengeSolving, + challenge_feedback: { + ...state.challenge_feedback, + [truth.uuid]: { + state: ChallengeFeedbackStatus.Message, + message: "Challenge should be solved", + }, + }, + }; + } - return tryRecoverSecret(newState); + return { + code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED, + hint: "got unexpected /truth/ response status", + http_status: resp.status, + } as ReducerStateError; +} + +async function solveChallenge( + state: ReducerStateRecovery, + ta: ActionArgsSolveChallengeRequest, +): Promise { + const recDoc: RecoveryDocument = state.verbatim_recovery_document!; + const truth = recDoc.escrow_methods.find( + (x) => x.uuid === state.selected_challenge_uuid, + ); + if (!truth) { + throw Error("truth for challenge not found"); + } + return requestTruth(state, truth, ta); } async function recoveryEnterUserAttributes( @@ -776,19 +806,7 @@ async function selectChallenge( throw "truth for challenge not found"; } - const url = new URL(`/truth/${truth.uuid}`, truth.url); - - const resp = await fetch(url.href, { - headers: { - "Anastasis-Truth-Decryption-Key": truth.truth_key, - }, - }); - - return { - ...state, - recovery_state: RecoveryStates.ChallengeSolving, - selected_challenge_uuid: ta.uuid, - }; + return requestTruth({ ...state, selected_challenge_uuid: ta.uuid }, truth); } async function backupSelectContinent( -- cgit v1.2.3