aboutsummaryrefslogtreecommitdiff
path: root/packages/anastasis-core/src/index.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-11-03 13:34:57 +0100
committerFlorian Dold <florian@dold.me>2021-11-03 13:34:57 +0100
commit04356cd23fef76d2020338d2b2b394095fdc2b14 (patch)
tree2c3081305fcdbc248a462d26fca37a54baa27a2f /packages/anastasis-core/src/index.ts
parentab6fd6c8c72ac674648ef66d7bcec01f7a232410 (diff)
downloadwallet-core-04356cd23fef76d2020338d2b2b394095fdc2b14.tar.xz
anastasis: refactor feedback types
Diffstat (limited to 'packages/anastasis-core/src/index.ts')
-rw-r--r--packages/anastasis-core/src/index.ts150
1 files changed, 84 insertions, 66 deletions
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<ReducerStateRecovery | ReducerStateError> {
- 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<ReducerStateRecovery | ReducerStateError> {
+ 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(