anastasis-core: crypto fixes

This commit is contained in:
Florian Dold 2021-10-19 18:39:38 +02:00
parent aac2bc389a
commit 26738d14f1
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 78 additions and 21 deletions

View File

@ -66,16 +66,12 @@ export function accountKeypairDerive(userId: UserIdentifier): AccountKeyPair {
// FIXME: the KDF invocation looks fishy, but that's what the C code presently does. // FIXME: the KDF invocation looks fishy, but that's what the C code presently does.
const d = kdfKw({ const d = kdfKw({
outputLength: 32, outputLength: 32,
ikm: stringToBytes("ver"), ikm: decodeCrock(userId),
salt: decodeCrock(userId), info: stringToBytes("ver"),
}); });
// FIXME: This bit twiddling seems wrong/unnecessary.
d[0] &= 248;
d[31] &= 127;
d[31] |= 64;
const pair = crypto_sign_keyPair_fromSeed(d); const pair = crypto_sign_keyPair_fromSeed(d);
return { return {
priv: encodeCrock(pair.secretKey), priv: encodeCrock(d),
pub: encodeCrock(pair.publicKey), pub: encodeCrock(pair.publicKey),
}; };
} }

View File

@ -1,10 +1,14 @@
import { import {
AmountString, AmountString,
buildSigPS,
codecForGetExchangeWithdrawalInfo, codecForGetExchangeWithdrawalInfo,
decodeCrock, decodeCrock,
eddsaSign,
encodeCrock, encodeCrock,
getRandomBytes, getRandomBytes,
hash,
TalerErrorCode, TalerErrorCode,
TalerSignaturePurpose,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { anastasisData } from "./anastasis-data.js"; import { anastasisData } from "./anastasis-data.js";
import { import {
@ -33,6 +37,7 @@ import {
ReducerStateBackupUserAttributesCollecting, ReducerStateBackupUserAttributesCollecting,
ReducerStateError, ReducerStateError,
ReducerStateRecovery, ReducerStateRecovery,
SuccessDetails,
} from "./reducer-types.js"; } from "./reducer-types.js";
import fetchPonyfill from "fetch-ponyfill"; import fetchPonyfill from "fetch-ponyfill";
import { import {
@ -43,6 +48,7 @@ import {
encryptTruth, encryptTruth,
PolicyKey, PolicyKey,
policyKeyDerive, policyKeyDerive,
PolicySalt,
UserIdentifier, UserIdentifier,
userIdentifierDerive, userIdentifierDerive,
} from "./crypto.js"; } from "./crypto.js";
@ -393,12 +399,17 @@ async function uploadSecret(
const coreSecret = state.core_secret?.value!; const coreSecret = state.core_secret?.value!;
// Truth key is `${methodIndex}/${providerUrl}` // Truth key is `${methodIndex}/${providerUrl}`
const truthMetadataMap: Record<string, TruthMetaData> = {}; const truthMetadataMap: Record<string, TruthMetaData> = {};
const policyKeys: PolicyKey[] = []; const policyKeys: PolicyKey[] = [];
const policySalts: PolicySalt[] = [];
// truth UUIDs for every policy.
const policyUuids: string[][] = [];
for (let policyIndex = 0; policyIndex < policies.length; policyIndex++) { for (let policyIndex = 0; policyIndex < policies.length; policyIndex++) {
const pol = policies[policyIndex]; const pol = policies[policyIndex];
const policySalt = encodeCrock(getRandomBytes(64)); const policySalt = encodeCrock(getRandomBytes(64));
const keyShares: string[] = []; const keyShares: string[] = [];
const methUuids: string[] = [];
for (let methIndex = 0; methIndex < pol.methods.length; methIndex++) { for (let methIndex = 0; methIndex < pol.methods.length; methIndex++) {
const meth = pol.methods[methIndex]; const meth = pol.methods[methIndex];
const truthKey = `${meth.authentication_method}:${meth.provider}`; const truthKey = `${meth.authentication_method}:${meth.provider}`;
@ -416,10 +427,12 @@ async function uploadSecret(
pol_method_index: methIndex, pol_method_index: methIndex,
policy_index: policyIndex, policy_index: policyIndex,
}; };
methUuids.push(tm.uuid);
truthMetadataMap[truthKey] = tm; truthMetadataMap[truthKey] = tm;
} }
const policyKey = await policyKeyDerive(keyShares, policySalt); const policyKey = await policyKeyDerive(keyShares, policySalt);
policyKeys.push(policyKey); policyKeys.push(policyKey);
policySalts.push(policySalt);
} }
const csr = await coreSecretEncrypt(policyKeys, coreSecret); const csr = await coreSecretEncrypt(policyKeys, coreSecret);
@ -472,6 +485,13 @@ async function uploadSecret(
body: JSON.stringify(tur), body: JSON.stringify(tur),
}); });
if (resp.status !== 204) {
return {
code: TalerErrorCode.ANASTASIS_REDUCER_NETWORK_FAILED,
hint: "could not upload policy",
};
}
escrowMethods.push({ escrowMethods.push({
escrow_type: authMethod.type, escrow_type: authMethod.type,
instructions: authMethod.instructions, instructions: authMethod.instructions,
@ -494,30 +514,56 @@ async function uploadSecret(
policies: policies.map((x, i) => { policies: policies.map((x, i) => {
return { return {
master_key: csr.encMasterKeys[i], master_key: csr.encMasterKeys[i],
// FIXME: ... uuid: policyUuids[i],
uuid: [], salt: policySalts[i],
salt: undefined as any,
}; };
}), }),
}; };
const successDetails: SuccessDetails = {};
for (const prov of state.policy_providers!) { for (const prov of state.policy_providers!) {
const uid = uidMap[prov.provider_url] const uid = uidMap[prov.provider_url];
const acctKeypair = accountKeypairDerive(uid); const acctKeypair = accountKeypairDerive(uid);
const encRecoveryDoc = await encryptRecoveryDocument(uid, rd); const encRecoveryDoc = await encryptRecoveryDocument(uid, rd);
// FIXME: Upload recovery document. const bodyHash = hash(decodeCrock(encRecoveryDoc));
const sigPS = buildSigPS(TalerSignaturePurpose.ANASTASIS_POLICY_UPLOAD)
.put(bodyHash)
.build();
const sig = eddsaSign(sigPS, decodeCrock(acctKeypair.priv));
const resp = await fetch( const resp = await fetch(
new URL(`policy/${acctKeypair.pub}`, prov.provider_url).href, new URL(`policy/${acctKeypair.pub}`, prov.provider_url).href,
{ {
method: "POST", method: "POST",
headers: {
"Anastasis-Policy-Signature": encodeCrock(sig),
"If-None-Match": encodeCrock(bodyHash),
},
body: decodeCrock(encRecoveryDoc), body: decodeCrock(encRecoveryDoc),
}, },
); );
if (resp.status !== 204) {
return {
code: TalerErrorCode.ANASTASIS_REDUCER_NETWORK_FAILED,
hint: "could not upload policy",
};
}
let policyVersion = 0;
console.log(resp);
console.log(resp.headers);
console.log(resp.headers.get("Anastasis-Version"));
try {
policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
} catch (e) {}
successDetails[prov.provider_url] = {
policy_version: policyVersion,
};
} }
return { return {
code: 123, ...state,
hint: "not implemented", backup_state: BackupStates.BackupFinished,
success_details: successDetails,
}; };
} }
@ -703,6 +749,19 @@ export async function reduceAction(
}; };
} }
} }
if (state.backup_state === BackupStates.BackupFinished) {
if (action === "back") {
return {
...state,
backup_state: BackupStates.SecretEditing,
};
} else {
return {
code: TalerErrorCode.ANASTASIS_REDUCER_ACTION_INVALID,
hint: `Unsupported action '${action}'`,
};
}
}
return { return {
code: TalerErrorCode.ANASTASIS_REDUCER_ACTION_INVALID, code: TalerErrorCode.ANASTASIS_REDUCER_ACTION_INVALID,
hint: "Reducer action invalid", hint: "Reducer action invalid",

View File

@ -27,6 +27,12 @@ export interface PolicyProvider {
provider_url: string; provider_url: string;
} }
export interface SuccessDetails {
[provider_url: string]: {
policy_version: number;
};
}
export interface ReducerStateBackup { export interface ReducerStateBackup {
recovery_state?: undefined; recovery_state?: undefined;
backup_state: BackupStates; backup_state: BackupStates;
@ -47,11 +53,7 @@ export interface ReducerStateBackup {
* and that are actually used in policies. * and that are actually used in policies.
*/ */
policy_providers?: PolicyProvider[]; policy_providers?: PolicyProvider[];
success_details?: { success_details?: SuccessDetails;
[provider_url: string]: {
policy_version: number;
};
};
payments?: string[]; payments?: string[];
policy_payment_requests?: { policy_payment_requests?: {
payto: string; payto: string;

View File

@ -2926,7 +2926,8 @@ export function crypto_sign_keyPair_fromSeed(
secretKey: Uint8Array; secretKey: Uint8Array;
} { } {
checkArrayTypes(seed); checkArrayTypes(seed);
if (seed.length !== crypto_sign_SEEDBYTES) throw new Error("bad seed size"); if (seed.length !== crypto_sign_SEEDBYTES)
throw new Error(`bad seed size: ${seed.length}`);
const pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); const pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
const sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); const sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
for (let i = 0; i < 32; i++) sk[i] = seed[i]; for (let i = 0; i < 32; i++) sk[i] = seed[i];
@ -3015,4 +3016,3 @@ export function secretbox_open(
if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return undefined; if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return undefined;
return m.subarray(crypto_secretbox_ZEROBYTES); return m.subarray(crypto_secretbox_ZEROBYTES);
} }