anastasis-core: question hashing and policy expiration
This commit is contained in:
parent
51d54fdd91
commit
5dc0089392
@ -33,7 +33,10 @@ export type EddsaPublicKey = Flavor<string, "EddsaPublicKey">;
|
||||
export type EddsaPrivateKey = Flavor<string, "EddsaPrivateKey">;
|
||||
export type TruthUuid = Flavor<string, "TruthUuid">;
|
||||
export type SecureAnswerHash = Flavor<string, "SecureAnswerHash">;
|
||||
export type QuestionSalt = Flavor<string, "QuestionSalt">;
|
||||
/**
|
||||
* Truth-specific randomness, also called question salt sometimes.
|
||||
*/
|
||||
export type TruthSalt = Flavor<string, "TruthSalt">;
|
||||
/**
|
||||
* Truth key, found in the recovery document.
|
||||
*/
|
||||
@ -150,7 +153,7 @@ async function anastasisEncrypt(
|
||||
return encodeCrock(taConcat([nonceBuf, cipherText]));
|
||||
}
|
||||
|
||||
const asOpaque = (x: string): OpaqueData => x;
|
||||
export const asOpaque = (x: string): OpaqueData => x;
|
||||
const asEncryptedKeyShare = (x: OpaqueData): EncryptedKeyShare => x as string;
|
||||
const asEncryptedTruth = (x: OpaqueData): EncryptedTruth => x as string;
|
||||
|
||||
@ -216,7 +219,7 @@ export async function coreSecretEncrypt(
|
||||
export async function secureAnswerHash(
|
||||
answer: string,
|
||||
truthUuid: TruthUuid,
|
||||
questionSalt: QuestionSalt,
|
||||
questionSalt: TruthSalt,
|
||||
): Promise<SecureAnswerHash> {
|
||||
const powResult = await argon2id({
|
||||
hashLength: 64,
|
||||
|
@ -1,14 +1,15 @@
|
||||
import {
|
||||
AmountString,
|
||||
buildSigPS,
|
||||
codecForGetExchangeWithdrawalInfo,
|
||||
decodeCrock,
|
||||
eddsaSign,
|
||||
encodeCrock,
|
||||
getRandomBytes,
|
||||
hash,
|
||||
stringToBytes,
|
||||
TalerErrorCode,
|
||||
TalerSignaturePurpose,
|
||||
Timestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { anastasisData } from "./anastasis-data.js";
|
||||
import {
|
||||
@ -42,13 +43,19 @@ import {
|
||||
import fetchPonyfill from "fetch-ponyfill";
|
||||
import {
|
||||
accountKeypairDerive,
|
||||
asOpaque,
|
||||
coreSecretEncrypt,
|
||||
encryptKeyshare,
|
||||
encryptRecoveryDocument,
|
||||
encryptTruth,
|
||||
OpaqueData,
|
||||
PolicyKey,
|
||||
policyKeyDerive,
|
||||
PolicySalt,
|
||||
TruthSalt,
|
||||
secureAnswerHash,
|
||||
TruthKey,
|
||||
TruthUuid,
|
||||
UserIdentifier,
|
||||
userIdentifierDerive,
|
||||
} from "./crypto.js";
|
||||
@ -100,13 +107,13 @@ interface EscrowMethod {
|
||||
*/
|
||||
escrow_type: string;
|
||||
|
||||
// UUID of the escrow method (see /truth/ API below).
|
||||
// UUID of the escrow method.
|
||||
// 16 bytes base32-crock encoded.
|
||||
uuid: string;
|
||||
uuid: TruthUuid;
|
||||
|
||||
// Key used to encrypt the Truth this EscrowMethod is related to.
|
||||
// Client has to provide this key to the server when using /truth/.
|
||||
truth_key: string;
|
||||
truth_key: TruthKey;
|
||||
|
||||
// Salt used to encrypt the truth on the Anastasis server.
|
||||
salt: string;
|
||||
@ -117,11 +124,6 @@ interface EscrowMethod {
|
||||
|
||||
// The instructions to give to the user (i.e. the security question
|
||||
// if this is challenge-response).
|
||||
// (Q: as string in base32 encoding?)
|
||||
// (Q: what is the mime-type of this value?)
|
||||
//
|
||||
// The plaintext challenge is not revealed to the
|
||||
// Anastasis server.
|
||||
instructions: string;
|
||||
}
|
||||
|
||||
@ -388,7 +390,28 @@ interface TruthMetaData {
|
||||
/**
|
||||
* Truth-specific salt.
|
||||
*/
|
||||
salt: string;
|
||||
truth_salt: string;
|
||||
}
|
||||
|
||||
async function getTruthValue(
|
||||
authMethod: AuthMethod,
|
||||
truthUuid: string,
|
||||
questionSalt: TruthSalt,
|
||||
): Promise<OpaqueData> {
|
||||
switch (authMethod.type) {
|
||||
case "question": {
|
||||
return asOpaque(
|
||||
await secureAnswerHash(authMethod.challenge, truthUuid, questionSalt),
|
||||
);
|
||||
}
|
||||
case "sms":
|
||||
case "email":
|
||||
case "totp":
|
||||
case "iban":
|
||||
return encodeCrock(stringToBytes(authMethod.type));
|
||||
default:
|
||||
throw Error("unknown auth type");
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadSecret(
|
||||
@ -421,7 +444,7 @@ async function uploadSecret(
|
||||
const tm: TruthMetaData = {
|
||||
key_share: keyShare,
|
||||
nonce: encodeCrock(getRandomBytes(24)),
|
||||
salt: encodeCrock(getRandomBytes(16)),
|
||||
truth_salt: encodeCrock(getRandomBytes(16)),
|
||||
truth_key: encodeCrock(getRandomBytes(32)),
|
||||
uuid: encodeCrock(getRandomBytes(32)),
|
||||
pol_method_index: methIndex,
|
||||
@ -459,13 +482,18 @@ async function uploadSecret(
|
||||
const provider = state.authentication_providers![
|
||||
meth.provider
|
||||
] as AuthenticationProviderStatusOk;
|
||||
const truthValue = await getTruthValue(authMethod, tm.uuid, tm.truth_salt);
|
||||
const encryptedTruth = await encryptTruth(
|
||||
tm.nonce,
|
||||
tm.truth_key,
|
||||
authMethod.challenge,
|
||||
truthValue,
|
||||
);
|
||||
const uid = uidMap[meth.provider];
|
||||
const encryptedKeyShare = await encryptKeyshare(tm.key_share, uid, tm.salt);
|
||||
const encryptedKeyShare = await encryptKeyshare(
|
||||
tm.key_share,
|
||||
uid,
|
||||
tm.truth_salt,
|
||||
);
|
||||
console.log(
|
||||
"encrypted key share len",
|
||||
decodeCrock(encryptedKeyShare).length,
|
||||
@ -496,7 +524,7 @@ async function uploadSecret(
|
||||
escrow_type: authMethod.type,
|
||||
instructions: authMethod.instructions,
|
||||
provider_salt: provider.salt,
|
||||
salt: tm.salt,
|
||||
salt: tm.truth_salt,
|
||||
truth_key: tm.truth_key,
|
||||
url: meth.provider,
|
||||
uuid: tm.uuid,
|
||||
@ -549,14 +577,19 @@ async function uploadSecret(
|
||||
};
|
||||
}
|
||||
let policyVersion = 0;
|
||||
console.log(resp);
|
||||
console.log(resp.headers);
|
||||
console.log(resp.headers.get("Anastasis-Version"));
|
||||
let policyExpiration: Timestamp = { t_ms: 0 };
|
||||
try {
|
||||
policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
|
||||
} catch (e) {}
|
||||
try {
|
||||
policyExpiration = {
|
||||
t_ms:
|
||||
1000 * Number(resp.headers.get("Anastasis-Policy-Expiration") ?? "0"),
|
||||
};
|
||||
} catch (e) {}
|
||||
successDetails[prov.provider_url] = {
|
||||
policy_version: policyVersion,
|
||||
policy_expiration: policyExpiration,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Duration } from "@gnu-taler/taler-util";
|
||||
import { Duration, Timestamp } from "@gnu-taler/taler-util";
|
||||
|
||||
export type ReducerState =
|
||||
| ReducerStateBackup
|
||||
@ -30,6 +30,7 @@ export interface PolicyProvider {
|
||||
export interface SuccessDetails {
|
||||
[provider_url: string]: {
|
||||
policy_version: number;
|
||||
policy_expiration: Timestamp;
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user