commit b67cddf4eb3dcb2e58a028653780385b6607a5b4
Author: Савелий Савенок <56991906+Lisoveliy@users.noreply.github.com>
Date: Sat Nov 9 16:37:03 2024 +0300
feat: ported totp.js library to zepp quick-js
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3e21f7f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+.DS_Store
+node_modules/**
+dist/*
+npm-debug.log
+yarn-debug.log*
+yarn-error.log*
+yarn.lock
+package-lock.json
+selenium-debug.log
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
\ No newline at end of file
diff --git a/app-side/i18n/en-US.po b/app-side/i18n/en-US.po
new file mode 100644
index 0000000..9eabe48
--- /dev/null
+++ b/app-side/i18n/en-US.po
@@ -0,0 +1,2 @@
+msgid "example"
+msgstr "This is an example in app-side"
\ No newline at end of file
diff --git a/app-side/index.js b/app-side/index.js
new file mode 100644
index 0000000..1e298b2
--- /dev/null
+++ b/app-side/index.js
@@ -0,0 +1,13 @@
+import { gettext } from 'i18n'
+
+AppSideService({
+ onInit() {
+ console.log(gettext('example'))
+ },
+
+ onRun() {
+ },
+
+ onDestroy() {
+ }
+})
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..13d6c22
--- /dev/null
+++ b/app.js
@@ -0,0 +1,10 @@
+App({
+ globalData: {},
+ onCreate(options) {
+ console.log('app on create invoke')
+ },
+
+ onDestroy(options) {
+ console.log('app on destroy invoke')
+ }
+})
\ No newline at end of file
diff --git a/app.json b/app.json
new file mode 100644
index 0000000..62100cc
--- /dev/null
+++ b/app.json
@@ -0,0 +1,55 @@
+{
+ "configVersion": "v3",
+ "app": {
+ "appId": 25087,
+ "appName": "totpfit",
+ "appType": "app",
+ "version": {
+ "code": 1,
+ "name": "1.0.0"
+ },
+ "icon": "icon.png",
+ "vender": "zepp",
+ "description": "TOTP Authenticator for Amazfit devices"
+ },
+ "permissions": [
+ "data:os.device.info"
+ ],
+ "runtime": {
+ "apiVersion": {
+ "compatible": "3.0.0",
+ "target": "3.0.0",
+ "minVersion": "3.0"
+ }
+ },
+ "targets": {
+ "default": {
+ "module": {
+ "page": {
+ "pages": [
+ "page/index",
+ "page/tip"
+ ]
+ },
+ "app-side": {
+ "path": "app-side/index"
+ },
+ "setting": {
+ "path": "setting/index"
+ }
+ },
+ "platforms": [
+ {
+ "st": "s",
+ "dw": 390
+ }
+ ]
+ }
+ },
+ "i18n": {
+ "en-US": {
+ "appName": "TOTPFit"
+ }
+ },
+ "defaultLanguage": "en-US"
+}
diff --git a/assets/default.b/icon.png b/assets/default.b/icon.png
new file mode 100644
index 0000000..5a8e20b
Binary files /dev/null and b/assets/default.b/icon.png differ
diff --git a/assets/default.r/icon.png b/assets/default.r/icon.png
new file mode 100644
index 0000000..5a8e20b
Binary files /dev/null and b/assets/default.r/icon.png differ
diff --git a/assets/default.s/icon.png b/assets/default.s/icon.png
new file mode 100644
index 0000000..5a8e20b
Binary files /dev/null and b/assets/default.s/icon.png differ
diff --git a/global.d.ts b/global.d.ts
new file mode 100644
index 0000000..ead47d2
--- /dev/null
+++ b/global.d.ts
@@ -0,0 +1 @@
+///
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..1bd80d8
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "checkJs": true
+ },
+ "exclude": ["node_modules", "**/node_modules/*"],
+ "files": ["node_modules/@zeppos/device-types/dist/index.d.ts"]
+}
diff --git a/lib/thirty-two/.npmignore b/lib/thirty-two/.npmignore
new file mode 100644
index 0000000..3ee8de4
--- /dev/null
+++ b/lib/thirty-two/.npmignore
@@ -0,0 +1,4 @@
+*.kpf
+*~
+\#*
+node_modules
diff --git a/lib/thirty-two/LICENSE.txt b/lib/thirty-two/LICENSE.txt
new file mode 100644
index 0000000..364e7fa
--- /dev/null
+++ b/lib/thirty-two/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2011, Chris Umbel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/lib/thirty-two/Makefile b/lib/thirty-two/Makefile
new file mode 100644
index 0000000..6011230
--- /dev/null
+++ b/lib/thirty-two/Makefile
@@ -0,0 +1,24 @@
+# Copyright (c) 2011, Chris Umbel
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+SHELL := /bin/bash
+
+test:
+ jasmine-node spec/
diff --git a/lib/thirty-two/README.md b/lib/thirty-two/README.md
new file mode 100644
index 0000000..fd81e38
--- /dev/null
+++ b/lib/thirty-two/README.md
@@ -0,0 +1,15 @@
+# thirty-two
+
+Implementation of RFC 3548 Base32 encoding/decoding for node.
+
+## Installation
+
+ npm install thirty-two
+
+## Usage
+
+ var base32 = require('thirty-two');
+ base32.encode('node');
+ // output: NZXWIZI=
+ base32.decode('NZXWIZI=');
+ //output: node
diff --git a/lib/thirty-two/index.js b/lib/thirty-two/index.js
new file mode 100644
index 0000000..e0eebe2
--- /dev/null
+++ b/lib/thirty-two/index.js
@@ -0,0 +1,23 @@
+/*
+Copyright (c) 2011, Chris Umbel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+export {encode, decode} from './lib/thirty-two/thirty-two.js'
\ No newline at end of file
diff --git a/lib/thirty-two/lib/thirty-two/thirty-two.js b/lib/thirty-two/lib/thirty-two/thirty-two.js
new file mode 100644
index 0000000..2887dfa
--- /dev/null
+++ b/lib/thirty-two/lib/thirty-two/thirty-two.js
@@ -0,0 +1,128 @@
+/*
+Copyright (c) 2011, Chris Umbel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+'use strict';
+
+var charTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+var byteTable = [
+ 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff
+];
+
+function quintetCount(buff) {
+ var quintets = Math.floor(buff.length / 5);
+ return buff.length % 5 === 0 ? quintets: quintets + 1;
+}
+
+export function encode(plain) {
+ if(!Buffer.isBuffer(plain)){
+ plain = new Buffer(plain);
+ }
+ var i = 0;
+ var j = 0;
+ var shiftIndex = 0;
+ var digit = 0;
+ var encoded = new Buffer(quintetCount(plain) * 8);
+
+ /* byte by byte isn't as pretty as quintet by quintet but tests a bit
+ faster. will have to revisit. */
+ while(i < plain.length) {
+ var current = plain[i];
+
+ if(shiftIndex > 3) {
+ digit = current & (0xff >> shiftIndex);
+ shiftIndex = (shiftIndex + 5) % 8;
+ digit = (digit << shiftIndex) | ((i + 1 < plain.length) ?
+ plain[i + 1] : 0) >> (8 - shiftIndex);
+ i++;
+ } else {
+ digit = (current >> (8 - (shiftIndex + 5))) & 0x1f;
+ shiftIndex = (shiftIndex + 5) % 8;
+ if(shiftIndex === 0) i++;
+ }
+
+ encoded[j] = charTable.charCodeAt(digit);
+ j++;
+ }
+
+ for(i = j; i < encoded.length; i++) {
+ encoded[i] = 0x3d; //'='.charCodeAt(0)
+ }
+
+ return encoded;
+};
+
+export function decode(encoded) {
+ var shiftIndex = 0;
+ var plainDigit = 0;
+ var plainChar;
+ var plainPos = 0;
+ if(!Buffer.isBuffer(encoded)){
+ encoded = new Buffer(encoded);
+ }
+ var decoded = new Buffer(Math.ceil(encoded.length * 5 / 8));
+
+ /* byte by byte isn't as pretty as octet by octet but tests a bit
+ faster. will have to revisit. */
+ for(var i = 0; i < encoded.length; i++) {
+ if(encoded[i] === 0x3d){ //'='
+ break;
+ }
+
+ var encodedByte = encoded[i] - 0x30;
+
+ if(encodedByte < byteTable.length) {
+ plainDigit = byteTable[encodedByte];
+
+ if(shiftIndex <= 3) {
+ shiftIndex = (shiftIndex + 5) % 8;
+
+ if(shiftIndex === 0) {
+ plainChar |= plainDigit;
+ decoded[plainPos] = plainChar;
+ plainPos++;
+ plainChar = 0;
+ } else {
+ plainChar |= 0xff & (plainDigit << (8 - shiftIndex));
+ }
+ } else {
+ shiftIndex = (shiftIndex + 5) % 8;
+ plainChar |= 0xff & (plainDigit >>> shiftIndex);
+ decoded[plainPos] = plainChar;
+ plainPos++;
+
+ plainChar = 0xff & (plainDigit << (8 - shiftIndex));
+ }
+ } else {
+ throw new Error('Invalid input - it is not base32 encoded string');
+ }
+ }
+
+ return decoded.slice(0, plainPos);
+};
diff --git a/lib/thirty-two/package.json b/lib/thirty-two/package.json
new file mode 100644
index 0000000..fc341c7
--- /dev/null
+++ b/lib/thirty-two/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "thirty-two",
+ "description": "Implementation RFC 3548 Base32 encoding/decoding for node.",
+ "version": "1.0.2",
+ "engines": {
+ "node": ">=0.2.6"
+ },
+ "author": "Chris Umbel ",
+ "keywords": ["base32", "encoding"],
+ "main": "./lib/thirty-two/index.js",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/chrisumbel/thirty-two.git"
+ }
+}
diff --git a/lib/thirty-two/spec/thirty-two_spec.js b/lib/thirty-two/spec/thirty-two_spec.js
new file mode 100644
index 0000000..a0e0c0a
--- /dev/null
+++ b/lib/thirty-two/spec/thirty-two_spec.js
@@ -0,0 +1,63 @@
+/*
+Copyright (c) 2011, Chris Umbel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+if(!expect){
+ function expect(a){
+ return {
+ toBe: function(b){
+ require('assert').strictEqual(a, b);
+ }
+ };
+ }
+}
+var base32 = require('../lib/thirty-two/');
+
+describe('thirty-two', function() {
+ it('should encode', function() {
+ expect(base32.encode('a').toString()).toBe('ME======');
+ expect(base32.encode('be').toString()).toBe('MJSQ====');
+ expect(base32.encode('bee').toString()).toBe('MJSWK===');
+ expect(base32.encode('beer').toString()).toBe('MJSWK4Q=');
+ expect(base32.encode('beers').toString()).toBe('MJSWK4TT');
+ expect(base32.encode('beers 1').toString()).toBe('MJSWK4TTEAYQ====');
+ expect(base32.encode('shockingly dismissed').toString()).toBe('ONUG6Y3LNFXGO3DZEBSGS43NNFZXGZLE');
+ });
+
+
+ it('should decode', function() {
+ expect(base32.decode('ME======').toString()).toBe('a');
+ expect(base32.decode('MJSQ====').toString()).toBe('be');
+ expect(base32.decode('ONXW4===').toString()).toBe('son');
+ expect(base32.decode('MJSWK===').toString()).toBe('bee');
+ expect(base32.decode('MJSWK4Q=').toString()).toBe('beer');
+ expect(base32.decode('MJSWK4TT').toString()).toBe('beers');
+ expect(base32.decode('mjswK4TT').toString()).toBe('beers');
+ expect(base32.decode('MJSWK4TTN5XA====').toString()).toBe('beerson');
+ expect(base32.decode('MJSWK4TTEAYQ====').toString()).toBe('beers 1');
+ expect(base32.decode('ONUG6Y3LNFXGO3DZEBSGS43NNFZXGZLE').toString()).toBe('shockingly dismissed');
+ });
+
+ it('should be binary safe', function() {
+ expect(base32.decode(base32.encode(new Buffer([0x00, 0xff, 0x88]))).toString('hex')).toBe('00ff88');
+ expect(base32.encode(new Buffer("f61e1f998d69151de8334dbe753ab17ae831c13849a6aecd95d0a4e5dc25", 'hex')).toString()).toBe('6YPB7GMNNEKR32BTJW7HKOVRPLUDDQJYJGTK5TMV2CSOLXBF');
+ expect(base32.decode('6YPB7GMNNEKR32BTJW7HKOVRPLUDDQJYJGTK5TMV2CSOLXBF').toString('hex')).toBe('f61e1f998d69151de8334dbe753ab17ae831c13849a6aecd95d0a4e5dc25');
+ });
+});
diff --git a/lib/totp.js/.travis.yml b/lib/totp.js/.travis.yml
new file mode 100644
index 0000000..efb0983
--- /dev/null
+++ b/lib/totp.js/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+ - "8"
diff --git a/lib/totp.js/README.md b/lib/totp.js/README.md
new file mode 100644
index 0000000..4cfac1e
--- /dev/null
+++ b/lib/totp.js/README.md
@@ -0,0 +1,5 @@
+# totp.js
+Two-factor authentication implementation in pure javascript. One-time password generator (HOTP/TOTP) with support for Google Authenticator.
+
+[](https://travis-ci.org/wuyanxin/totp.js)
+
diff --git a/lib/totp.js/index.js b/lib/totp.js/index.js
new file mode 100644
index 0000000..17fed34
--- /dev/null
+++ b/lib/totp.js/index.js
@@ -0,0 +1,9 @@
+/*
+ * @Author: wuyanxin
+ * @Date: 2018-03-21 23:12:14
+ * @Last Modified by: wuyanxin
+ * @Last Modified time: 2018-03-21 23:12:14
+ */
+
+export {HOTP} from './lib/hotp.js'
+export {TOTP} from './lib/totp.js'
diff --git a/lib/totp.js/lib/hotp.js b/lib/totp.js/lib/hotp.js
new file mode 100644
index 0000000..3c1c9b9
--- /dev/null
+++ b/lib/totp.js/lib/hotp.js
@@ -0,0 +1,77 @@
+/*
+ * @Author: wuyanxin
+ * @Date: 2018-03-21 22:25:37
+ * @Last Modified by: wuyanxin
+ * @Last Modified time: 2018-03-21 22:29:43
+ */
+
+import {encode, decode} from '../../thirty-two/index.js'
+import jsSHA from 'jssha'
+
+export class HOTP {
+
+ /**
+ *
+ * @param {string} key secret key
+ * @param {*} digit lenth of otp code
+ */
+ constructor(key, digit = 6) {
+ this.key = key;
+ this.digit = digit;
+ }
+
+ /**
+ * generate secret key
+ * @param {int} len
+ */
+ static randomKey(len = 16) {
+ const str = Math.random().toString(36);
+ return encode(str).toString().substr(0, len);
+ }
+
+ /**
+ * generate a OTP base on HMAC-SHA-1
+ * @param {int} movingFactor counter
+ */
+ genOTP(movingFactor) {
+ const hmacSha = new jsSHA('SHA-1', 'BYTES');
+ hmacSha.setHMACKey(decode(this.key).toString(), 'BYTES');
+
+ const factorByte = this._factor2ByteText(movingFactor);
+ hmacSha.update(factorByte);
+
+ const hmac_result = hmacSha.getHMAC('BYTES');
+ return this._truncat(hmac_result);
+ }
+
+ /**
+ * verify a OPT code
+ * @param {string} opt opt code
+ * @param {int} movingFactor counter
+ */
+ verify(opt, movingFactor) {
+ return opt === this.genOTP(movingFactor);
+ }
+
+ _truncat(hmac_result) {
+ const offset = hmac_result[19].charCodeAt() & 0xf;
+ const bin_code = (hmac_result[offset].charCodeAt() & 0x7f) << 24
+ | (hmac_result[offset+1].charCodeAt() & 0xff) << 16
+ | (hmac_result[offset+2].charCodeAt() & 0xff) << 8
+ | (hmac_result[offset+3].charCodeAt() & 0xff);
+ let otp = (bin_code % 10 ** this.digit).toString();
+ while (otp.length < this.digit) {
+ otp = '0' + otp;
+ }
+ return otp;
+ }
+
+ _factor2ByteText(movingFactor) {
+ const text = new Array(8);
+ for (let i = text.length - 1; i >= 0; i--) {
+ text[i] = String.fromCharCode(movingFactor & 0xFF);
+ movingFactor >>= 8;
+ }
+ return text.join('');
+ }
+}
\ No newline at end of file
diff --git a/lib/totp.js/lib/totp.js b/lib/totp.js/lib/totp.js
new file mode 100644
index 0000000..ebae234
--- /dev/null
+++ b/lib/totp.js/lib/totp.js
@@ -0,0 +1,24 @@
+/*
+ * @Author: wuyanxin
+ * @Date: 2018-03-21 22:25:42
+ * @Last Modified by: wuyanxin
+ * @Last Modified time: 2018-03-21 22:42:28
+ */
+
+import {HOTP} from "./hotp.js";
+
+export class TOTP extends HOTP {
+ constructor(key){
+ super(key)
+ }
+
+ genOTP(timeStep = 30, t0 = 0) {
+ const T = Math.floor((Date.now() / 1000 - t0) / timeStep);
+ return super.genOTP(T);
+ }
+
+ verify(otp, timeStep = 30, t0 = 0) {
+ return otp === this.genOTP(timeStep, t0);
+ }
+
+}
\ No newline at end of file
diff --git a/lib/totp.js/package.json b/lib/totp.js/package.json
new file mode 100644
index 0000000..798a589
--- /dev/null
+++ b/lib/totp.js/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "totp.js",
+ "version": "0.0.1",
+ "description": "Two-factor authentication implementation in pure javascript. One-time password generator (HOTP/TOTP) with support for Google Authenticator. ",
+ "main": "index.js",
+ "scripts": {
+ "test": "./node_modules/.bin/mocha"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/wuyanxin/totp.js.git"
+ },
+ "keywords": [
+ "otp",
+ "hotp",
+ "totp",
+ "Two-factor authentication",
+ "Google authenticator"
+ ],
+ "author": "wuyanxin",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/wuyanxin/totp.js/issues"
+ },
+ "homepage": "https://github.com/wuyanxin/totp.js#readme",
+ "dependencies": {
+ "jssha": "^2.3.1",
+ "thirty-two": "^1.0.2"
+ },
+ "devDependencies": {
+ "mocha": "^5.0.4"
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5dac011
--- /dev/null
+++ b/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "totpfit",
+ "version": "1.0.0",
+ "description": "TOTP Authenticator for Amazfit devices",
+ "main": "app.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "MIT",
+ "devDependencies": {
+ "@zeppos/device-types": "^3.0.0"
+ },
+ "dependencies": {
+ "jssha": "^3.3.1"
+ }
+}
diff --git a/page/i18n/en-US.po b/page/i18n/en-US.po
new file mode 100644
index 0000000..e168a33
--- /dev/null
+++ b/page/i18n/en-US.po
@@ -0,0 +1,2 @@
+msgid "example"
+msgstr "This is an example in device"
\ No newline at end of file
diff --git a/page/index.js b/page/index.js
new file mode 100644
index 0000000..d1a6b86
--- /dev/null
+++ b/page/index.js
@@ -0,0 +1,35 @@
+import { getDeviceInfo } from '@zos/device'
+import { push } from '@zos/router'
+import { createWidget, widget } from '@zos/ui'
+
+import {TOTP} from '../lib/totp.js/lib/totp.js'
+
+Page({
+ build() {
+
+ const totp = new TOTP('asdasd')
+
+ const {width, height} = getDeviceInfo()
+ createWidget(widget.TEXT, {
+ x: width / 2 - 30,
+ y: height / 2 - 60,
+ text: totp.genOTP()
+ })
+ createWidget(widget.BUTTON,{
+ x: width / 2 - 40,
+ y: height / 2 - 20,
+ w: 80,
+ h: 80,
+ text: '+',
+ radius: 50,
+ text_size: 40,
+ normal_color: 0x303030,
+ press_color: 0x181c18,
+ click_func: () => {
+ push({
+ url: 'page/tip'
+ })
+ }
+ })
+ }
+})
diff --git a/page/index.s.layout.js b/page/index.s.layout.js
new file mode 100644
index 0000000..7bf6175
--- /dev/null
+++ b/page/index.s.layout.js
@@ -0,0 +1,6 @@
+import { px } from "@zos/utils";
+
+export const TEXT_STYLE = {
+ x: px(0),
+ y: px(0),
+}
diff --git a/page/tip.js b/page/tip.js
new file mode 100644
index 0000000..29ab95a
--- /dev/null
+++ b/page/tip.js
@@ -0,0 +1,29 @@
+import { createWidget, widget, align } from "@zos/ui";
+import { getDeviceInfo } from "@zos/device";
+import { onGesture, GESTURE_LEFT } from '@zos/interaction'
+import { back } from "@zos/router";
+Page({
+ onInit(){
+ onGesture({
+ callback(event) {
+ if(event === GESTURE_LEFT){
+ back()
+ }
+ }
+ })
+ },
+ build() {
+ console.log("Page tip opened")
+ const {width, height} = getDeviceInfo()
+ createWidget(widget.TEXT, {
+ x: 0,
+ w: width,
+ h: height,
+ color: 0xffffff,
+ text_size: 30,
+ align_h: align.CENTER_H,
+ align_v: align.CENTER_V,
+ text: 'To add TOTP record open\n settings on Zepp app'
+ })
+ }
+})
\ No newline at end of file
diff --git a/setting/i18n/en-US.po b/setting/i18n/en-US.po
new file mode 100644
index 0000000..9eabe48
--- /dev/null
+++ b/setting/i18n/en-US.po
@@ -0,0 +1,2 @@
+msgid "example"
+msgstr "This is an example in app-side"
\ No newline at end of file
diff --git a/setting/index.js b/setting/index.js
new file mode 100644
index 0000000..eaf57c2
--- /dev/null
+++ b/setting/index.js
@@ -0,0 +1,7 @@
+import { gettext } from 'i18n'
+
+AppSettingsPage({
+ build() {
+ console.log(gettext('example'))
+ }
+})
\ No newline at end of file