gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: totp qr code


From: gnunet
Subject: [taler-wallet-core] branch master updated: totp qr code
Date: Tue, 02 Nov 2021 14:13:17 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new a4cdc02e totp qr code
a4cdc02e is described below

commit a4cdc02e5017ba587c169cb28a7e7927fc64c7cf
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Nov 2 10:12:52 2021 -0300

    totp qr code
---
 packages/anastasis-webui/package.json              |  1 +
 packages/anastasis-webui/src/declaration.d.ts      |  3 +-
 .../pages/home/ReviewPoliciesScreen.stories.tsx    |  2 +-
 .../AuthMethodTotpSetup.stories.tsx                |  6 +--
 .../home/authMethodSetup/AuthMethodTotpSetup.tsx   | 54 +++++++++++++++++----
 .../src/pages/home/authMethodSetup/totp.ts         | 56 ++++++++++++++++++++++
 pnpm-lock.yaml                                     |  6 +++
 7 files changed, 112 insertions(+), 16 deletions(-)

diff --git a/packages/anastasis-webui/package.json 
b/packages/anastasis-webui/package.json
index 2f2577a9..b032c563 100644
--- a/packages/anastasis-webui/package.json
+++ b/packages/anastasis-webui/package.json
@@ -53,6 +53,7 @@
     "eslint-config-preact": "^1.1.1",
     "jest": "^26.2.2",
     "jest-preset-preact": "^4.0.2",
+    "jssha": "^3.2.0",
     "preact-cli": "^3.2.2",
     "sass": "^1.32.13",
     "sass-loader": "^10.1.1",
diff --git a/packages/anastasis-webui/src/declaration.d.ts 
b/packages/anastasis-webui/src/declaration.d.ts
index edd3a07a..2c4b7cb3 100644
--- a/packages/anastasis-webui/src/declaration.d.ts
+++ b/packages/anastasis-webui/src/declaration.d.ts
@@ -17,5 +17,4 @@ declare module '*.png' {
 declare module 'jed' {
     const x: any;
     export = x;
-  }
-  
\ No newline at end of file
+}
diff --git 
a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx 
b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
index 00701132..5ba0c937 100644
--- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
@@ -244,5 +244,5 @@ export const SomePoliciesWithMethods = 
createExample(TestedComponent, {
     type: "question",
     instructions: "Does P equal NP?",
     challenge: "C5SP8"
-  }]
+}]
 } as ReducerState);
diff --git 
a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx
 
b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx
index 3447e3d6..4e46b600 100644
--- 
a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx
+++ 
b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.stories.tsx
@@ -45,7 +45,7 @@ export const WithOneExample = 
createExample(TestedComponent[type].screen, reduce
   configured: [{
     challenge: 'qwe',
     type,
-    instructions: 'instr',
+    instructions: 'Enter 8 digits code for "Anastasis"',
     remove: () => null
   }]
 });
@@ -53,12 +53,12 @@ export const WithMoreExample = 
createExample(TestedComponent[type].screen, reduc
   configured: [{
     challenge: 'qwe',
     type,
-    instructions: 'instr',
+    instructions: 'Enter 8 digits code for "Anastasis1"',
     remove: () => null
   },{
     challenge: 'qwe',
     type,
-    instructions: 'instr',
+    instructions: 'Enter 8 digits code for "Anastasis2"',
     remove: () => null
   }]
 });
diff --git 
a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx
 
b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx
index bbffedad..db656e63 100644
--- 
a/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx
+++ 
b/packages/anastasis-webui/src/pages/home/authMethodSetup/AuthMethodTotpSetup.tsx
@@ -4,36 +4,70 @@ import {
   stringToBytes
 } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
+import { useMemo, useState } from "preact/hooks";
 import { AuthMethodSetupProps } from "../AuthenticationEditorScreen";
 import { AnastasisClientFrame } from "../index";
 import { TextInput } from "../../../components/fields/TextInput";
 import { QR } from "../../../components/QR";
+import { base32enc, computeTOTPandCheck } from "./totp";
+
+export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: 
AuthMethodSetupProps): VNode {
+  const [name, setName] = useState("anastasis");
+  const [test, setTest] = useState("");
+  const digits = 8
+  const secretKey = useMemo(() => {
+    const array = new Uint8Array(32)
+    return window.crypto.getRandomValues(array)
+  }, [])
+  const secret32 = base32enc(secretKey);
+  const totpURL = `otpauth://totp/${name}?digits=${digits}&secret=${secret32}`
 
-export function AuthMethodTotpSetup({addAuthMethod, cancel, configured}: 
AuthMethodSetupProps): VNode {
-  const [name, setName] = useState("");
   const addTotpAuth = (): void => addAuthMethod({
     authentication_method: {
       type: "totp",
-      instructions: `Enter code for ${name}`,
-      challenge: encodeCrock(stringToBytes(name)),
+      instructions: `Enter ${digits} digits code for ${name}`,
+      challenge: encodeCrock(stringToBytes(totpURL)),
     },
   });
-  const errors = !name ? 'The TOTP name is missing' : undefined;
+
+  const testCodeMatches = computeTOTPandCheck(secretKey, 8, parseInt(test, 
10));
+
+  const errors = !name ? 'The TOTP name is missing' : (
+    !testCodeMatches ? 'The test code doesnt match' : undefined
+  );
   return (
     <AnastasisClientFrame hideNav title="Add TOTP authentication">
       <p>
-        For Time-based One-Time Password (TOTP) authentication, you need to 
set 
-        a name for the TOTP secret. Then, you must scan the generated QR code 
+        For Time-based One-Time Password (TOTP) authentication, you need to set
+        a name for the TOTP secret. Then, you must scan the generated QR code
         with your TOTP App to import the TOTP secret into your TOTP App.
       </p>
-      <div>
+      <div class="block">
         <TextInput
           label="TOTP Name"
           grabFocus
           bind={[name, setName]} />
       </div>
-      <QR text={`sometext ${name}`} />
+      <div style={{ height: 300 }}>
+        <QR text={totpURL} />
+      </div>
+      <p>
+        After scanning the code with your TOTP App, test it in the input below.
+      </p>
+      <TextInput
+        label="Test code"
+        bind={[test, setTest]} />
+      {configured.length > 0 && <section class="section">
+        <div class="block">
+          Your TOTP numbers:
+        </div><div class="block">
+          {configured.map((c, i) => {
+            return <div key={i} class="box" style={{ display: 'flex', 
justifyContent: 'space-between' }}>
+              <p style={{ marginTop: 'auto', marginBottom: 'auto' 
}}>{c.instructions}</p>
+              <div><button class="button is-danger" 
onClick={c.remove}>Delete</button></div>
+            </div>
+          })}
+        </div></section>}
       <div>
         <div style={{ marginTop: '2em', display: 'flex', justifyContent: 
'space-between' }}>
           <button class="button" onClick={cancel}>Cancel</button>
diff --git a/packages/anastasis-webui/src/pages/home/authMethodSetup/totp.ts 
b/packages/anastasis-webui/src/pages/home/authMethodSetup/totp.ts
new file mode 100644
index 00000000..0bc3feaf
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/authMethodSetup/totp.ts
@@ -0,0 +1,56 @@
+/* eslint-disable @typescript-eslint/camelcase */
+import jssha from 'jssha'
+
+const SEARCH_RANGE = 16
+const timeStep = 30
+
+export function computeTOTPandCheck(secretKey: Uint8Array, digits: number, 
code: number): boolean {
+  const now = new Date().getTime()
+  const epoch = Math.floor(Math.round(now / 1000.0) / timeStep);
+
+  for (let ms = -SEARCH_RANGE; ms < SEARCH_RANGE; ms++) {
+    const movingFactor = (epoch + ms).toString(16).padStart(16, "0");
+
+    const hmacSha = new jssha('SHA-1', 'HEX', { hmacKey: { value: secretKey, 
format: 'UINT8ARRAY' } });
+    hmacSha.update(movingFactor);
+    const hmac_text = hmacSha.getHMAC('UINT8ARRAY');
+
+    const offset = (hmac_text[hmac_text.length - 1] & 0xf)
+
+    const otp = ((
+      (hmac_text[offset + 0] << 24) +
+      (hmac_text[offset + 1] << 16) +
+      (hmac_text[offset + 2] << 8) +
+      (hmac_text[offset + 3])
+    ) & 0x7fffffff) % Math.pow(10, digits)
+
+    if (otp == code) return true
+  }
+  return false
+}
+
+const encTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".split('')
+export function base32enc(buffer: Uint8Array): string {
+  let rpos = 0
+  let bits = 0
+  let vbit = 0
+
+  let result = ""
+  while ((rpos < buffer.length) || (vbit > 0)) {
+    if ((rpos < buffer.length) && (vbit < 5)) {
+      bits = (bits << 8) | buffer[rpos++];
+      vbit += 8;
+    }
+    if (vbit < 5) {
+      bits <<= (5 - vbit);
+      vbit = 5;
+    }
+    result += encTable__[(bits >> (vbit - 5)) & 31];
+    vbit -= 5;
+  }
+  return result
+}
+
+// const array = new Uint8Array(256)
+// const secretKey = window.crypto.getRandomValues(array)
+// console.log(base32enc(secretKey))
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e38b0e85..e22c3067 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -57,6 +57,7 @@ importers:
       jed: 1.1.1
       jest: ^26.2.2
       jest-preset-preact: ^4.0.2
+      jssha: ^3.2.0
       preact: ^10.3.1
       preact-cli: ^3.2.2
       preact-render-to-string: ^5.1.4
@@ -96,6 +97,7 @@ importers:
       eslint-config-preact: 1.1.4_eslint@6.8.0+typescript@3.9.10
       jest: 26.6.3
       jest-preset-preact: 4.0.2_9b3f24ae35a87c3c82fffbe3fdf70e1e
+      jssha: 3.2.0
       preact-cli: 3.2.2_8d1b4ee21ca5a56b4aabd4a3e659b2d7
       sass: 1.43.2
       sass-loader: 10.2.0_sass@1.43.2
@@ -15313,6 +15315,10 @@ packages:
       verror: 1.10.0
     dev: true
 
+  /jssha/3.2.0:
+    resolution: {integrity: 
sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==}
+    dev: true
+
   /jsx-ast-utils/3.2.0:
     resolution: {integrity: 
sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==}
     engines: {node: '>=4.0'}

-- 
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]