feat: added google migration support (BETA) v.1.2.0

This commit is contained in:
Pavel-Savely Savianok 2025-03-17 15:30:03 +03:00
parent fb6ece773c
commit 21c7646c71
8 changed files with 99 additions and 13 deletions

View File

@ -1,6 +1,17 @@
# TOTPFIT # TOTPFIT
### Another 2FAuthenticator based on TOTP for Zepp Amazfit GTS 4 ### Another 2FAuthenticator based on TOTP for Zepp Amazfit GTS 4
Features: ![alt text](docs/assets/image2.png)
### Features:
- Supports of otpauth links with parameters "issuer", "algorithm", "digits", "period" - Supports of otpauth links with parameters "issuer", "algorithm", "digits", "period"
- Addition/Edition/Deletion of TOTPs from mobile settings app - Addition/Edition/Deletion of TOTPs from mobile settings app
### Google Migration Support:
- Support of google migration links formated: ```otpauth-migration://offline?data=...``` (BETA)
### Screenshots:
![alt text](docs/assets/image2.png)
![alt text](docs/assets/image.png)

BIN
docs/assets/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/assets/image2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -9,7 +9,6 @@ export function decodeVarint(buffer, offset) {
} }
byte = buffer[offset++]; byte = buffer[offset++];
console.log(this)
const multiplier = this.BigInt(2) ** this.BigInt(shift); const multiplier = this.BigInt(2) ** this.BigInt(shift);
const thisByteValue = this.BigInt(byte & 0x7f) * multiplier; const thisByteValue = this.BigInt(byte & 0x7f) * multiplier;

View File

@ -25,6 +25,32 @@ function leftpad(str, len, pad) {
); );
} }
export function encode(bytes) {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
let bits = 0;
let value = 0;
let output = '';
for (let i = 0; i < bytes.length; i++) {
value = (value << 8) | bytes[i];
bits += 8;
while (bits >= 5) {
output += alphabet[(value >>> (bits - 5)) & 0x1F];
bits -= 5;
}
}
if (bits > 0) {
output += alphabet[(value << (5 - bits)) & 0x1F];
}
const paddingLength = (8 - (output.length % 8)) % 8;
output += '='.repeat(paddingLength);
return output;
}
export function base64decode(base64) { export function base64decode(base64) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let result = []; let result = [];

View File

@ -6,7 +6,6 @@ import { LocalStorage } from "@zos/storage";
const app = getApp(); const app = getApp();
let waitForFetch = true; let waitForFetch = true;
let localStorage = new LocalStorage();
Page( Page(
BasePage({ BasePage({
@ -14,6 +13,8 @@ Page(
this.getTOTPData() this.getTOTPData()
.then((x) => { .then((x) => {
app._options.globalData.TOTPS = JSON.parse(x) ?? []; app._options.globalData.TOTPS = JSON.parse(x) ?? [];
let localStorage = new LocalStorage();
localStorage.setItem( localStorage.setItem(
"TOTPs", "TOTPs",
JSON.stringify(app._options.globalData.TOTPS) JSON.stringify(app._options.globalData.TOTPS)
@ -22,6 +23,7 @@ Page(
}) })
.catch((x) => { .catch((x) => {
console.log(`Init failed: ${x}`); console.log(`Init failed: ${x}`);
let localStorage = new LocalStorage();
app._options.globalData.TOTPS = JSON.parse( app._options.globalData.TOTPS = JSON.parse(
localStorage.getItem("TOTPs", null) ?? [] localStorage.getItem("TOTPs", null) ?? []
); );

View File

@ -13,12 +13,16 @@ AppSettingsPage({
placeholder: "otpauth://", placeholder: "otpauth://",
label: "Add new OTP Link", label: "Add new OTP Link",
onChange: (changes) => { onChange: (changes) => {
var link = getTOTPByLink(changes); let link = getTOTPByLink(changes);
if (link == null) { if (link == null) {
console.log("link is invalid"); console.log("link is invalid");
return; return;
} }
storage.push(link);
if(Array.isArray(link))
storage.push(...link);
else storage.push(link);
updateStorage(storage); updateStorage(storage);
}, },
labelStyle: { labelStyle: {
@ -80,7 +84,11 @@ function GetTOTPList(storage) {
label: "Change OTP link", label: "Change OTP link",
onChange: (changes) => { onChange: (changes) => {
try { try {
storage[elementId] = getTOTPByLink(changes); let link = getTOTPByLink(changes);
if(Array.isArray(link))
return;
storage[elementId] = link;
updateStorage(storage); updateStorage(storage);
} catch (err) { } catch (err) {
console.log(err); console.log(err);

View File

@ -1,6 +1,6 @@
import { decodeProto } from "../../lib/protobuf-decoder/protobufDecoder"; import { decodeProto, TYPES } from "../../lib/protobuf-decoder/protobufDecoder";
import { TOTP } from "../../lib/totp-quickjs"; import { TOTP } from "../../lib/totp-quickjs";
import { base64decode } from "../../lib/totp-quickjs/base32decoder"; import { base64decode, encode } from "../../lib/totp-quickjs/base32decoder";
const otpauthScheme = "otpauth:/"; const otpauthScheme = "otpauth:/";
const googleMigrationScheme = "otpauth-migration:/"; const googleMigrationScheme = "otpauth-migration:/";
@ -40,7 +40,7 @@ function getByOtpauthScheme(link){
if (secret === undefined) throw new Error("Secret not defined"); if (secret === undefined) throw new Error("Secret not defined");
if(issuer == client){ if(issuer == client){
issuer = args[3].split("issuer=")[1]?.split("&")[0] issuer = args[3].split("issuer=")[1]?.split("&")[0];
} }
issuer = decodeURIComponent(issuer); issuer = decodeURIComponent(issuer);
@ -65,9 +65,49 @@ function getByGoogleMigrationScheme(link){
let data = link.split("data=")[1]; //Returns base64 encoded data let data = link.split("data=")[1]; //Returns base64 encoded data
data = decodeURIComponent(data); data = decodeURIComponent(data);
console.log(data)
let decode = base64decode(data); let decode = base64decode(data);
console.log(decode)
let proto = decodeProto(decode); let proto = decodeProto(decode);
console.log(proto);
let protoTotps = [];
proto.parts.forEach(part => {
if(part.type == TYPES.LENDELIM){
protoTotps.push(decodeProto(part.value));
}
});
let totps = [];
protoTotps.forEach(x => {
let type = x.parts.filter(x => x.index == 6)[0]; //find type of OTP
if(type.value !== '2'){
console.log("ERR: it's a not TOTP record")
return;
}
let secret = x.parts.filter(x => x.index == 1)[0].value;
secret = encode(secret);
let name = bytesToString(x.parts.filter(x => x.index == 2)[0].value);
let issuer = bytesToString(x.parts.filter(x => x.index == 3)[0].value);
totps.push(new TOTP(
secret,
issuer,
name,
6,
30,
0,
"SHA-1"
));
});
return totps;
}
function bytesToString(bytes) {
let str = '';
for (let i = 0; i < bytes.length; i++) {
str += String.fromCharCode(bytes[i]);
}
return str;
} }