summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-10-19 18:39:38 +0200
committerFlorian Dold <florian@dold.me>2021-10-19 18:39:47 +0200
commit26738d14f1d60d9aa7471d0be267d286a4644069 (patch)
tree1b35547ec784aef11006569dc252c4d2fb171316
parentaac2bc389a32b2e91494d097a3f3fc9560ddbfbc (diff)
downloadwallet-core-26738d14f1d60d9aa7471d0be267d286a4644069.tar.gz
wallet-core-26738d14f1d60d9aa7471d0be267d286a4644069.tar.bz2
wallet-core-26738d14f1d60d9aa7471d0be267d286a4644069.zip
anastasis-core: crypto fixes
-rw-r--r--packages/anastasis-core/src/crypto.ts10
-rw-r--r--packages/anastasis-core/src/index.ts73
-rw-r--r--packages/anastasis-core/src/reducer-types.ts12
-rw-r--r--packages/taler-util/src/nacl-fast.ts4
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<string, TruthMetaData> = {};
+
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);
}
-