gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 02/02: anastasis-core: compatible secret upload


From: gnunet
Subject: [taler-wallet-core] 02/02: anastasis-core: compatible secret upload
Date: Tue, 19 Oct 2021 23:26:37 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

commit 6c5d32be7458a6423b8a2b0ab8c3002394620f14
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Oct 19 23:26:29 2021 +0200

    anastasis-core: compatible secret upload
---
 packages/anastasis-core/package.json               |  1 +
 packages/anastasis-core/src/crypto.test.ts         |  1 +
 packages/anastasis-core/src/crypto.ts              | 17 ++++---
 packages/anastasis-core/src/index.ts               | 59 +++++++++++++++++-----
 packages/anastasis-core/src/reducer-types.ts       | 10 ++--
 .../src/hooks/use-anastasis-reducer.ts             |  2 +-
 .../src/pages/home/SecretEditorScreen.tsx          | 23 +++++----
 pnpm-lock.yaml                                     |  2 +
 8 files changed, 80 insertions(+), 35 deletions(-)

diff --git a/packages/anastasis-core/package.json 
b/packages/anastasis-core/package.json
index f4b611ed..8dbef2d4 100644
--- a/packages/anastasis-core/package.json
+++ b/packages/anastasis-core/package.json
@@ -23,6 +23,7 @@
   "dependencies": {
     "@gnu-taler/taler-util": "workspace:^0.8.3",
     "fetch-ponyfill": "^7.1.0",
+    "fflate": "^0.6.0",
     "hash-wasm": "^4.9.0",
     "node-fetch": "^3.0.0"
   },
diff --git a/packages/anastasis-core/src/crypto.test.ts 
b/packages/anastasis-core/src/crypto.test.ts
index e535b7b2..1c255014 100644
--- a/packages/anastasis-core/src/crypto.test.ts
+++ b/packages/anastasis-core/src/crypto.test.ts
@@ -1,6 +1,7 @@
 import test from "ava";
 import {
   accountKeypairDerive,
+  encryptKeyshare,
   encryptTruth,
   policyKeyDerive,
   secureAnswerHash,
diff --git a/packages/anastasis-core/src/crypto.ts 
b/packages/anastasis-core/src/crypto.ts
index f7cfa965..63de795b 100644
--- a/packages/anastasis-core/src/crypto.ts
+++ b/packages/anastasis-core/src/crypto.ts
@@ -10,6 +10,7 @@ import {
   crypto_sign_keyPair_fromSeed,
   stringToBytes,
 } from "@gnu-taler/taler-util";
+import { gzipSync } from "fflate";
 import { argon2id } from "hash-wasm";
 
 export type Flavor<T, FlavorT extends string> = T & {
@@ -84,21 +85,25 @@ export function accountKeypairDerive(userId: 
UserIdentifier): AccountKeyPair {
   };
 }
 
+/**
+ * Encrypt the recovery document.
+ * 
+ * The caller should first compress the recovery doc.
+ */
 export async function encryptRecoveryDocument(
   userId: UserIdentifier,
-  recoveryDoc: any,
+  recoveryDocData: OpaqueData,
 ): Promise<OpaqueData> {
-  const plaintext = stringToBytes(JSON.stringify(recoveryDoc));
   const nonce = encodeCrock(getRandomBytes(nonceSize));
   return anastasisEncrypt(
     nonce,
     asOpaque(userId),
-    encodeCrock(plaintext),
+    recoveryDocData,
     "erd",
   );
 }
 
-function taConcat(chunks: Uint8Array[]): Uint8Array {
+export function typedArrayConcat(chunks: Uint8Array[]): Uint8Array {
   let payloadLen = 0;
   for (const c of chunks) {
     payloadLen += c.byteLength;
@@ -120,7 +125,7 @@ export async function policyKeyDerive(
   const chunks = keyShares.map((x) => decodeCrock(x));
   const polKey = kdfKw({
     outputLength: 64,
-    ikm: taConcat(chunks),
+    ikm: typedArrayConcat(chunks),
     salt: decodeCrock(policySalt),
     info: stringToBytes("anastasis-policy-key-derive"),
   });
@@ -150,7 +155,7 @@ async function anastasisEncrypt(
   const key = await deriveKey(keySeed, nonce, salt);
   const nonceBuf = decodeCrock(nonce);
   const cipherText = secretbox(decodeCrock(plaintext), decodeCrock(nonce), 
key);
-  return encodeCrock(taConcat([nonceBuf, cipherText]));
+  return encodeCrock(typedArrayConcat([nonceBuf, cipherText]));
 }
 
 export const asOpaque = (x: string): OpaqueData => x;
diff --git a/packages/anastasis-core/src/index.ts 
b/packages/anastasis-core/src/index.ts
index d8071e99..2909cf61 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -1,6 +1,7 @@
 import {
   AmountString,
   buildSigPS,
+  bytesToString,
   decodeCrock,
   eddsaSign,
   encodeCrock,
@@ -58,7 +59,9 @@ import {
   TruthUuid,
   UserIdentifier,
   userIdentifierDerive,
+  typedArrayConcat,
 } from "./crypto.js";
+import { zlibSync } from "fflate";
 
 const { fetch, Request, Response, Headers } = fetchPonyfill({});
 
@@ -93,7 +96,7 @@ interface DecryptionPolicy {
   /**
    * List of escrow methods identified by their UUID.
    */
-  uuid: string[];
+  uuids: string[];
 }
 
 interface EscrowMethod {
@@ -115,8 +118,10 @@ interface EscrowMethod {
   // Client has to provide this key to the server when using /truth/.
   truth_key: TruthKey;
 
-  // Salt used to encrypt the truth on the Anastasis server.
-  salt: string;
+  /**
+   * Salt to hash the security question answer if applicable.
+   */
+  truth_salt: TruthSalt;
 
   // Salt from the provider to derive the user ID
   // at this provider.
@@ -401,7 +406,11 @@ async function getTruthValue(
   switch (authMethod.type) {
     case "question": {
       return asOpaque(
-        await secureAnswerHash(authMethod.challenge, truthUuid, questionSalt),
+        await secureAnswerHash(
+          bytesToString(decodeCrock(authMethod.challenge)),
+          truthUuid,
+          questionSalt,
+        ),
       );
     }
     case "sms":
@@ -414,12 +423,28 @@ async function getTruthValue(
   }
 }
 
+/**
+ * Compress the recovery document and add a size header.
+ */
+async function compressRecoveryDoc(rd: any): Promise<Uint8Array> {
+  console.log("recovery document", rd);
+  const docBytes = stringToBytes(JSON.stringify(rd));
+  console.log("plain doc length", docBytes.length);
+  const sizeHeaderBuf = new ArrayBuffer(4);
+  const dvbuf = new DataView(sizeHeaderBuf);
+  dvbuf.setUint32(0, docBytes.length, false);
+  const zippedDoc = zlibSync(docBytes);
+  return typedArrayConcat([new Uint8Array(sizeHeaderBuf), zippedDoc]);
+}
+
 async function uploadSecret(
   state: ReducerStateBackup,
 ): Promise<ReducerStateBackup | ReducerStateError> {
   const policies = state.policies!;
   const secretName = state.secret_name!;
-  const coreSecret = state.core_secret?.value!;
+  const coreSecret: OpaqueData = encodeCrock(
+    stringToBytes(JSON.stringify(state.core_secret!)),
+  );
   // Truth key is `${methodIndex}/${providerUrl}`
   const truthMetadataMap: Record<string, TruthMetaData> = {};
 
@@ -435,8 +460,8 @@ async function uploadSecret(
     const methUuids: string[] = [];
     for (let methIndex = 0; methIndex < pol.methods.length; methIndex++) {
       const meth = pol.methods[methIndex];
-      const truthKey = `${meth.authentication_method}:${meth.provider}`;
-      if (truthMetadataMap[truthKey]) {
+      const truthReference = `${meth.authentication_method}:${meth.provider}`;
+      if (truthMetadataMap[truthReference]) {
         continue;
       }
       const keyShare = encodeCrock(getRandomBytes(32));
@@ -445,15 +470,16 @@ async function uploadSecret(
         key_share: keyShare,
         nonce: encodeCrock(getRandomBytes(24)),
         truth_salt: encodeCrock(getRandomBytes(16)),
-        truth_key: encodeCrock(getRandomBytes(32)),
+        truth_key: encodeCrock(getRandomBytes(64)),
         uuid: encodeCrock(getRandomBytes(32)),
         pol_method_index: methIndex,
         policy_index: policyIndex,
       };
       methUuids.push(tm.uuid);
-      truthMetadataMap[truthKey] = tm;
+      truthMetadataMap[truthReference] = tm;
     }
     const policyKey = await policyKeyDerive(keyShares, policySalt);
+    policyUuids.push(methUuids);
     policyKeys.push(policyKey);
     policySalts.push(policySalt);
   }
@@ -492,7 +518,9 @@ async function uploadSecret(
     const encryptedKeyShare = await encryptKeyshare(
       tm.key_share,
       uid,
-      tm.truth_salt,
+      authMethod.type === "question"
+        ? bytesToString(decodeCrock(authMethod.challenge))
+        : undefined,
     );
     console.log(
       "encrypted key share len",
@@ -524,7 +552,7 @@ async function uploadSecret(
       escrow_type: authMethod.type,
       instructions: authMethod.instructions,
       provider_salt: provider.salt,
-      salt: tm.truth_salt,
+      truth_salt: tm.truth_salt,
       truth_key: tm.truth_key,
       url: meth.provider,
       uuid: tm.uuid,
@@ -542,7 +570,7 @@ async function uploadSecret(
     policies: policies.map((x, i) => {
       return {
         master_key: csr.encMasterKeys[i],
-        uuid: policyUuids[i],
+        uuids: policyUuids[i],
         salt: policySalts[i],
       };
     }),
@@ -553,7 +581,12 @@ async function uploadSecret(
   for (const prov of state.policy_providers!) {
     const uid = uidMap[prov.provider_url];
     const acctKeypair = accountKeypairDerive(uid);
-    const encRecoveryDoc = await encryptRecoveryDocument(uid, rd);
+    const zippedDoc = await compressRecoveryDoc(rd);
+    console.log("zipped doc", zippedDoc);
+    const encRecoveryDoc = await encryptRecoveryDocument(
+      uid,
+      encodeCrock(zippedDoc),
+    );
     const bodyHash = hash(decodeCrock(encRecoveryDoc));
     const sigPS = buildSigPS(TalerSignaturePurpose.ANASTASIS_POLICY_UPLOAD)
       .put(bodyHash)
diff --git a/packages/anastasis-core/src/reducer-types.ts 
b/packages/anastasis-core/src/reducer-types.ts
index 92b1c532..44761ea0 100644
--- a/packages/anastasis-core/src/reducer-types.ts
+++ b/packages/anastasis-core/src/reducer-types.ts
@@ -34,6 +34,11 @@ export interface SuccessDetails {
   };
 }
 
+export interface CoreSecret {
+  mime: string;
+  value: string;
+}
+
 export interface ReducerStateBackup {
   recovery_state?: undefined;
   backup_state: BackupStates;
@@ -61,10 +66,7 @@ export interface ReducerStateBackup {
     provider: string;
   }[];
 
-  core_secret?: {
-    mime: string;
-    value: string;
-  };
+  core_secret?: CoreSecret;
 
   expiration?: Duration;
 }
diff --git a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts 
b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
index 72424e82..4a242a2e 100644
--- a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
+++ b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
@@ -3,7 +3,7 @@ import { BackupStates, getBackupStartState, 
getRecoveryStartState, RecoveryState
 import { useState } from "preact/hooks";
 
 const reducerBaseUrl = "http://localhost:5000/";;
-const remoteReducer = true;
+const remoteReducer = false;
 
 interface AnastasisState {
   reducerState: ReducerState | undefined;
diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx 
b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
index 2963930f..086d4921 100644
--- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
@@ -1,20 +1,19 @@
 /* eslint-disable @typescript-eslint/camelcase */
-import {
-  encodeCrock,
-  stringToBytes
-} from "@gnu-taler/taler-util";
+import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { BackupReducerProps, AnastasisClientFrame, LabeledInput } from 
"./index";
+import {
+  BackupReducerProps,
+  AnastasisClientFrame,
+  LabeledInput,
+} from "./index";
 
 export function SecretEditorScreen(props: BackupReducerProps): VNode {
   const { reducer } = props;
   const [secretName, setSecretName] = useState(
-    props.backupState.secret_name ?? ""
-  );
-  const [secretValue, setSecretValue] = useState(
-    props.backupState.core_secret?.value ?? "" ?? ""
+    props.backupState.secret_name ?? "",
   );
+  const [secretValue, setSecretValue] = useState("");
   const secretNext = (): void => {
     reducer.runTransaction(async (tx) => {
       await tx.transition("enter_secret_name", {
@@ -41,12 +40,14 @@ export function SecretEditorScreen(props: 
BackupReducerProps): VNode {
         <LabeledInput
           label="Secret Name:"
           grabFocus
-          bind={[secretName, setSecretName]} />
+          bind={[secretName, setSecretName]}
+        />
       </div>
       <div>
         <LabeledInput
           label="Secret Value:"
-          bind={[secretValue, setSecretValue]} />
+          bind={[secretValue, setSecretValue]}
+        />
       </div>
     </AnastasisClientFrame>
   );
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 30b9e8d0..f5f77e57 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -17,12 +17,14 @@ importers:
       '@gnu-taler/taler-util': workspace:^0.8.3
       ava: ^3.15.0
       fetch-ponyfill: ^7.1.0
+      fflate: ^0.6.0
       hash-wasm: ^4.9.0
       node-fetch: ^3.0.0
       typescript: ^4.4.3
     dependencies:
       '@gnu-taler/taler-util': link:../taler-util
       fetch-ponyfill: 7.1.0
+      fflate: 0.6.0
       hash-wasm: 4.9.0
       node-fetch: 3.0.0
     devDependencies:

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]