TOTPFit/setting/utils/queryParser.js

106 lines
3.3 KiB
JavaScript

import { decodeProto, TYPES } from "../../lib/protobuf-decoder/protobufDecoder";
import { TOTP } from "../../lib/totp-quickjs";
import { base64decode, encode } from "../../lib/totp-quickjs/base32decoder";
const otpauthScheme = "otpauth:/";
const googleMigrationScheme = "otpauth-migration:/";
export function getTOTPByLink(link) {
if (link.includes(otpauthScheme)) return getByOtpauthScheme(link);
if (link.includes(googleMigrationScheme))
return getByGoogleMigrationScheme(link);
return null;
}
function getHashType(algorithm) {
if (algorithm == "SHA1") return "SHA-1";
if (algorithm == "SHA256") return "SHA-256";
if (algorithm == "SHA512") return "SHA-512";
else return "SHA-1";
}
function getByOtpauthScheme(link) {
try {
let args = link.split("/", otpauthScheme.length);
let type = args[2]; //Returns 'hotp' or 'totp'
let issuer = args[3].split(":")[0]?.split("?")[0]; //Returns issuer
let client =
args[3].split(":")[1]?.split("?")[0] ??
args[3].split(":")[0]?.split("?")[0]; //Returns client
let secret = args[3].split("secret=")[1]?.split("&")[0]; //Returns secret
let period = args[3].split("period=")[1]?.split("&")[0]; //Returns period
let digits = args[3].split("digit=")[1]?.split("&")[0]; //Returns digits
let algorithm = args[3].split("algorithm=")[1]?.split("&")[0]; //Returns algorithm
let offset = args[3].split("offset=")[1]?.split("&")[0] ?? 0; //Returns offset
if (type.toLowerCase() != "totp")
throw new Error("Type is not valid, requires 'TOTP'");
if (secret === undefined) throw new Error("Secret not defined");
if (issuer == client) {
issuer = args[3].split("issuer=")[1]?.split("&")[0];
}
issuer = decodeURIComponent(issuer);
client = decodeURIComponent(client);
return new TOTP(
secret,
issuer,
client,
Number(digits),
Number(period),
Number(offset),
getHashType(algorithm),
);
} catch (err) {
console.log(err);
return null;
}
}
function getByGoogleMigrationScheme(link) {
let data = link.split("data=")[1]; //Returns base64 encoded data
data = decodeURIComponent(data);
let decode = base64decode(data);
let proto = decodeProto(decode);
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;
}