summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-11-04 16:53:04 +0100
committerFlorian Dold <florian@dold.me>2021-11-04 16:53:11 +0100
commit6d6679e33849d551b9da07d5058dc09c474c66b7 (patch)
tree192410793b30057a0c52612f513c007ecabbfacc /packages
parent83622bd65a0ebd7c22555fb2cfdb542e20d044b9 (diff)
downloadwallet-core-6d6679e33849d551b9da07d5058dc09c474c66b7.tar.gz
wallet-core-6d6679e33849d551b9da07d5058dc09c474c66b7.tar.bz2
wallet-core-6d6679e33849d551b9da07d5058dc09c474c66b7.zip
anastasis-core: support pin-type answers
Diffstat (limited to 'packages')
-rw-r--r--packages/anastasis-core/src/crypto.ts5
-rw-r--r--packages/anastasis-core/src/index.ts68
-rw-r--r--packages/anastasis-core/src/recovery-document-types.ts13
-rw-r--r--packages/anastasis-core/src/reducer-types.ts36
4 files changed, 106 insertions, 16 deletions
diff --git a/packages/anastasis-core/src/crypto.ts b/packages/anastasis-core/src/crypto.ts
index da8338636..9689e4f2d 100644
--- a/packages/anastasis-core/src/crypto.ts
+++ b/packages/anastasis-core/src/crypto.ts
@@ -10,6 +10,7 @@ import {
crypto_sign_keyPair_fromSeed,
stringToBytes,
secretbox_open,
+ hash,
} from "@gnu-taler/taler-util";
import { gzipSync } from "fflate";
import { argon2id } from "hash-wasm";
@@ -283,6 +284,10 @@ export async function coreSecretEncrypt(
};
}
+export async function pinAnswerHash(pin: number): Promise<SecureAnswerHash> {
+ return encodeCrock(hash(stringToBytes(pin.toString())));
+}
+
export async function secureAnswerHash(
answer: string,
truthUuid: TruthUuid,
diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts
index fd04eb4d7..4c8ac0d31 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -86,11 +86,19 @@ import {
decryptKeyShare,
KeyShare,
coreSecretRecover,
+ pinAnswerHash,
} from "./crypto.js";
import { unzlibSync, zlibSync } from "fflate";
-import { EscrowMethod, RecoveryDocument } from "./recovery-document-types.js";
+import {
+ ChallengeType,
+ EscrowMethod,
+ RecoveryDocument,
+} from "./recovery-document-types.js";
import { ProviderInfo, suggestPolicies } from "./policy-suggestion.js";
-import { ChallengeFeedback, ChallengeFeedbackStatus } from "./challenge-feedback-types.js";
+import {
+ ChallengeFeedback,
+ ChallengeFeedbackStatus,
+} from "./challenge-feedback-types.js";
const { fetch } = fetchPonyfill({});
@@ -473,7 +481,7 @@ async function uploadSecret(
}
escrowMethods.push({
- escrow_type: authMethod.type,
+ escrow_type: authMethod.type as any,
instructions: authMethod.instructions,
provider_salt: provider.salt,
truth_salt: tm.truth_salt,
@@ -697,11 +705,43 @@ async function requestTruth(
const url = new URL(`/truth/${truth.uuid}`, truth.url);
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),
- );
+ let respHash: string;
+ switch (truth.escrow_type) {
+ case ChallengeType.Question:
+ if ("answer" in solveRequest) {
+ respHash = await secureAnswerHash(
+ solveRequest.answer,
+ truth.uuid,
+ truth.truth_salt,
+ );
+ } else {
+ throw Error("unsupported answer request");
+ }
+ break;
+ case ChallengeType.Email:
+ case ChallengeType.Sms:
+ case ChallengeType.Post:
+ case ChallengeType.Totp: {
+ if ("answer" in solveRequest) {
+ const s = solveRequest.answer.trim().replace(/^A-/, "");
+ let pin: number;
+ try {
+ pin = Number.parseInt(s);
+ } catch (e) {
+ throw Error("invalid pin format");
+ }
+ respHash = await pinAnswerHash(pin);
+ } else if ("pin" in solveRequest) {
+ respHash = await pinAnswerHash(solveRequest.pin);
+ } else {
+ throw Error("unsupported answer request");
+ }
+ break;
+ }
+ default:
+ throw Error("unsupported challenge type");
+ }
+ url.searchParams.set("response", respHash);
}
const resp = await fetch(url.href, {
@@ -711,10 +751,14 @@ async function requestTruth(
});
if (resp.status === HttpStatusCode.Ok) {
- const answerSalt =
- solveRequest && truth.escrow_type === "question"
- ? solveRequest.answer
- : undefined;
+ let answerSalt: string | undefined = undefined;
+ if (
+ solveRequest &&
+ truth.escrow_type === "question" &&
+ "answer" in solveRequest
+ ) {
+ answerSalt = solveRequest.answer;
+ }
const userId = await userIdentifierDerive(
state.identity_attributes,
diff --git a/packages/anastasis-core/src/recovery-document-types.ts b/packages/anastasis-core/src/recovery-document-types.ts
index 74003ccb1..3dc4481ff 100644
--- a/packages/anastasis-core/src/recovery-document-types.ts
+++ b/packages/anastasis-core/src/recovery-document-types.ts
@@ -1,5 +1,14 @@
import { TruthKey, TruthSalt, TruthUuid } from "./crypto.js";
+export enum ChallengeType {
+ Question = "question",
+ Sms = "sms",
+ Email = "email",
+ Post = "post",
+ Totp = "totp",
+ Iban = "iban",
+}
+
export interface RecoveryDocument {
/**
* Human-readable name of the secret
@@ -9,7 +18,7 @@ export interface RecoveryDocument {
/**
* Encrypted core secret.
- *
+ *
* Variable-size length, base32-crock encoded.
*/
encrypted_core_secret: string;
@@ -56,7 +65,7 @@ export interface EscrowMethod {
/**
* Type of the escrow method (e.g. security question, SMS etc.).
*/
- escrow_type: string;
+ escrow_type: ChallengeType;
/**
* UUID of the escrow method.
diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts
index e0d311e88..19f7d431a 100644
--- a/packages/anastasis-core/src/reducer-types.ts
+++ b/packages/anastasis-core/src/reducer-types.ts
@@ -312,12 +312,44 @@ export interface ActionArgsSelectChallenge {
uuid: string;
}
-export type ActionArgsSolveChallengeRequest = SolveChallengeAnswerRequest;
-
+export type ActionArgsSolveChallengeRequest =
+ | SolveChallengeAnswerRequest
+ | SolveChallengePinRequest
+ | SolveChallengeHashRequest;
+
+/**
+ * Answer to a challenge.
+ *
+ * For "question" challenges, this is a string with the answer.
+ *
+ * For "sms" / "email" / "post" this is a numeric code with optionally
+ * the "A-" prefix.
+ */
export interface SolveChallengeAnswerRequest {
answer: string;
}
+/**
+ * Answer to a challenge that requires a numeric response.
+ *
+ * XXX: Should be deprecated in favor of just "answer".
+ */
+export interface SolveChallengePinRequest {
+ pin: number;
+}
+
+/**
+ * Answer to a challenge by directly providing the hash.
+ *
+ * XXX: When / why is this even used?
+ */
+ export interface SolveChallengeHashRequest {
+ /**
+ * Base32-crock encoded hash code.
+ */
+ hash: string;
+}
+
export interface PolicyMember {
authentication_method: number;
provider: string;