From 26738d14f1d60d9aa7471d0be267d286a4644069 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 19 Oct 2021 18:39:38 +0200 Subject: anastasis-core: crypto fixes --- packages/anastasis-core/src/crypto.ts | 10 ++-- packages/anastasis-core/src/index.ts | 73 +++++++++++++++++++++++++--- packages/anastasis-core/src/reducer-types.ts | 12 +++-- packages/taler-util/src/nacl-fast.ts | 4 +- 4 files changed, 78 insertions(+), 21 deletions(-) diff --git a/packages/anastasis-core/src/crypto.ts b/packages/anastasis-core/src/crypto.ts index 32cf470c4..1332f8492 100644 --- a/packages/anastasis-core/src/crypto.ts +++ b/packages/anastasis-core/src/crypto.ts @@ -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. const d = kdfKw({ outputLength: 32, - ikm: stringToBytes("ver"), - salt: decodeCrock(userId), + ikm: 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); return { - priv: encodeCrock(pair.secretKey), + priv: encodeCrock(d), pub: encodeCrock(pair.publicKey), }; } diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts index 8921433b7..7206f9122 100644 --- a/packages/anastasis-core/src/index.ts +++ b/packages/anastasis-core/src/index.ts @@ -1,10 +1,14 @@ import { AmountString, + buildSigPS, codecForGetExchangeWithdrawalInfo, decodeCrock, + eddsaSign, encodeCrock, getRandomBytes, + hash, TalerErrorCode, + TalerSignaturePurpose, } from "@gnu-taler/taler-util"; import { anastasisData } from "./anastasis-data.js"; import { @@ -33,6 +37,7 @@ import { ReducerStateBackupUserAttributesCollecting, ReducerStateError, ReducerStateRecovery, + SuccessDetails, } from "./reducer-types.js"; import fetchPonyfill from "fetch-ponyfill"; import { @@ -43,6 +48,7 @@ import { encryptTruth, PolicyKey, policyKeyDerive, + PolicySalt, UserIdentifier, userIdentifierDerive, } from "./crypto.js"; @@ -393,12 +399,17 @@ async function uploadSecret( const coreSecret = state.core_secret?.value!; // Truth key is `${methodIndex}/${providerUrl}` const truthMetadataMap: Record = {}; + const policyKeys: PolicyKey[] = []; + const policySalts: PolicySalt[] = []; + // truth UUIDs for every policy. + const policyUuids: string[][] = []; for (let policyIndex = 0; policyIndex < policies.length; policyIndex++) { const pol = policies[policyIndex]; const policySalt = encodeCrock(getRandomBytes(64)); const keyShares: string[] = []; + const methUuids: string[] = []; for (let methIndex = 0; methIndex < pol.methods.length; methIndex++) { const meth = pol.methods[methIndex]; const truthKey = `${meth.authentication_method}:${meth.provider}`; @@ -416,10 +427,12 @@ async function uploadSecret( pol_method_index: methIndex, policy_index: policyIndex, }; + methUuids.push(tm.uuid); truthMetadataMap[truthKey] = tm; } const policyKey = await policyKeyDerive(keyShares, policySalt); policyKeys.push(policyKey); + policySalts.push(policySalt); } const csr = await coreSecretEncrypt(policyKeys, coreSecret); @@ -472,6 +485,13 @@ async function uploadSecret( body: JSON.stringify(tur), }); + if (resp.status !== 204) { + return { + code: TalerErrorCode.ANASTASIS_REDUCER_NETWORK_FAILED, + hint: "could not upload policy", + }; + } + escrowMethods.push({ escrow_type: authMethod.type, instructions: authMethod.instructions, @@ -494,30 +514,56 @@ async function uploadSecret( policies: policies.map((x, i) => { return { master_key: csr.encMasterKeys[i], - // FIXME: ... - uuid: [], - salt: undefined as any, + uuid: policyUuids[i], + salt: policySalts[i], }; }), }; + const successDetails: SuccessDetails = {}; + for (const prov of state.policy_providers!) { - const uid = uidMap[prov.provider_url] + const uid = uidMap[prov.provider_url]; const acctKeypair = accountKeypairDerive(uid); 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( new URL(`policy/${acctKeypair.pub}`, prov.provider_url).href, { method: "POST", + headers: { + "Anastasis-Policy-Signature": encodeCrock(sig), + "If-None-Match": encodeCrock(bodyHash), + }, 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 { - code: 123, - hint: "not implemented", + ...state, + 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 { code: TalerErrorCode.ANASTASIS_REDUCER_ACTION_INVALID, hint: "Reducer action invalid", diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts index 0d1754bd9..1f4a2abea 100644 --- a/packages/anastasis-core/src/reducer-types.ts +++ b/packages/anastasis-core/src/reducer-types.ts @@ -27,6 +27,12 @@ export interface PolicyProvider { provider_url: string; } +export interface SuccessDetails { + [provider_url: string]: { + policy_version: number; + }; +} + export interface ReducerStateBackup { recovery_state?: undefined; backup_state: BackupStates; @@ -47,11 +53,7 @@ export interface ReducerStateBackup { * and that are actually used in policies. */ policy_providers?: PolicyProvider[]; - success_details?: { - [provider_url: string]: { - policy_version: number; - }; - }; + success_details?: SuccessDetails; payments?: string[]; policy_payment_requests?: { payto: string; diff --git a/packages/taler-util/src/nacl-fast.ts b/packages/taler-util/src/nacl-fast.ts index 6e721f32c..500ac11c9 100644 --- a/packages/taler-util/src/nacl-fast.ts +++ b/packages/taler-util/src/nacl-fast.ts @@ -2926,7 +2926,8 @@ export function crypto_sign_keyPair_fromSeed( secretKey: Uint8Array; } { 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 sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); 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; return m.subarray(crypto_secretbox_ZEROBYTES); } - -- cgit v1.2.3