anastasis-core: support pin-type answers

This commit is contained in:
Florian Dold 2021-11-04 16:53:04 +01:00
parent 83622bd65a
commit 6d6679e338
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 105 additions and 15 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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.

View File

@ -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;