feat: adding protobuf decoder for parse of google auth migrations
This commit is contained in:
parent
ccfd422061
commit
bd490d4642
36
lib/protobuf-decoder/hexUtils.js
Normal file
36
lib/protobuf-decoder/hexUtils.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Buffer } from 'buffer'
|
||||||
|
|
||||||
|
export function parseInput(input) {
|
||||||
|
const normalizedInput = input.replace(/\s/g, "");
|
||||||
|
const normalizedHexInput = normalizedInput.replace(/0x/g, "").toLowerCase();
|
||||||
|
if (isHex(normalizedHexInput)) {
|
||||||
|
return Buffer.from(normalizedHexInput, "hex");
|
||||||
|
} else {
|
||||||
|
return Buffer.from(normalizedInput, "base64");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isHex(string) {
|
||||||
|
let result = true;
|
||||||
|
for (const char of string) {
|
||||||
|
if (!((char >= "a" && char <= "f") || (char >= "0" && char <= "9"))) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bufferLeToBeHex(buffer) {
|
||||||
|
let output = "";
|
||||||
|
for (const v of buffer) {
|
||||||
|
const hex = v.toString(16);
|
||||||
|
if (hex.length === 1) {
|
||||||
|
output = "0" + hex + output;
|
||||||
|
} else {
|
||||||
|
output = hex + output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bufferToPrettyHex = b => [...b].map(c => c.toString(16).padStart(2, '0')).join(' ');
|
27
lib/protobuf-decoder/intUtils.js
Normal file
27
lib/protobuf-decoder/intUtils.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import JSBI from "jsbi";
|
||||||
|
|
||||||
|
export function interpretAsSignedType(n) {
|
||||||
|
// see https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/wire_format_lite.h#L857-L876
|
||||||
|
// however, this is a simpler equivalent formula
|
||||||
|
const isEven = JSBI.equal(JSBI.bitwiseAnd(n, JSBI.BigInt(1)), JSBI.BigInt(0));
|
||||||
|
if (isEven) {
|
||||||
|
return JSBI.divide(n, JSBI.BigInt(2));
|
||||||
|
} else {
|
||||||
|
return JSBI.multiply(
|
||||||
|
JSBI.BigInt(-1),
|
||||||
|
JSBI.divide(JSBI.add(n, JSBI.BigInt(1)), JSBI.BigInt(2))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function interpretAsTwosComplement(n, bits) {
|
||||||
|
const isTwosComplement = JSBI.equal(
|
||||||
|
JSBI.signedRightShift(n, JSBI.BigInt(bits - 1)),
|
||||||
|
JSBI.BigInt(1)
|
||||||
|
);
|
||||||
|
if (isTwosComplement) {
|
||||||
|
return JSBI.subtract(n, JSBI.leftShift(JSBI.BigInt(1), JSBI.BigInt(bits)));
|
||||||
|
} else {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
132
lib/protobuf-decoder/protobufDecoder.js
Normal file
132
lib/protobuf-decoder/protobufDecoder.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { decodeVarint } from "./varintUtils";
|
||||||
|
|
||||||
|
class BufferReader {
|
||||||
|
constructor(buffer) {
|
||||||
|
this.buffer = buffer;
|
||||||
|
this.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
readVarInt() {
|
||||||
|
const result = decodeVarint(this.buffer, this.offset);
|
||||||
|
this.offset += result.length;
|
||||||
|
|
||||||
|
return result.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
readBuffer(length) {
|
||||||
|
this.checkByte(length);
|
||||||
|
const result = this.buffer.slice(this.offset, this.offset + length);
|
||||||
|
this.offset += length;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// gRPC has some additional header - remove it
|
||||||
|
trySkipGrpcHeader() {
|
||||||
|
const backupOffset = this.offset;
|
||||||
|
|
||||||
|
if (this.buffer[this.offset] === 0 && this.leftBytes() >= 5) {
|
||||||
|
this.offset++;
|
||||||
|
const length = this.buffer.readInt32BE(this.offset);
|
||||||
|
this.offset += 4;
|
||||||
|
|
||||||
|
if (length > this.leftBytes()) {
|
||||||
|
// Something is wrong, revert
|
||||||
|
this.offset = backupOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leftBytes() {
|
||||||
|
return this.buffer.length - this.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkByte(length) {
|
||||||
|
const bytesAvailable = this.leftBytes();
|
||||||
|
if (length > bytesAvailable) {
|
||||||
|
throw new Error(
|
||||||
|
"Not enough bytes left. Requested: " +
|
||||||
|
length +
|
||||||
|
" left: " +
|
||||||
|
bytesAvailable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkpoint() {
|
||||||
|
this.savedOffset = this.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetToCheckpoint() {
|
||||||
|
this.offset = this.savedOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TYPES = {
|
||||||
|
VARINT: 0,
|
||||||
|
FIXED64: 1,
|
||||||
|
LENDELIM: 2,
|
||||||
|
FIXED32: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
export function decodeProto(buffer) {
|
||||||
|
const reader = new BufferReader(buffer);
|
||||||
|
const parts = [];
|
||||||
|
|
||||||
|
reader.trySkipGrpcHeader();
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (reader.leftBytes() > 0) {
|
||||||
|
reader.checkpoint();
|
||||||
|
|
||||||
|
const byteRange = [reader.offset];
|
||||||
|
const indexType = parseInt(reader.readVarInt().toString());
|
||||||
|
const type = indexType & 0b111;
|
||||||
|
const index = indexType >> 3;
|
||||||
|
|
||||||
|
let value;
|
||||||
|
if (type === TYPES.VARINT) {
|
||||||
|
value = reader.readVarInt().toString();
|
||||||
|
} else if (type === TYPES.LENDELIM) {
|
||||||
|
const length = parseInt(reader.readVarInt().toString());
|
||||||
|
value = reader.readBuffer(length);
|
||||||
|
} else if (type === TYPES.FIXED32) {
|
||||||
|
value = reader.readBuffer(4);
|
||||||
|
} else if (type === TYPES.FIXED64) {
|
||||||
|
value = reader.readBuffer(8);
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown type: " + type);
|
||||||
|
}
|
||||||
|
byteRange.push(reader.offset);
|
||||||
|
|
||||||
|
parts.push({
|
||||||
|
byteRange,
|
||||||
|
index,
|
||||||
|
type,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
reader.resetToCheckpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
parts,
|
||||||
|
leftOver: reader.readBuffer(reader.leftBytes())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function typeToString(type, subType) {
|
||||||
|
switch (type) {
|
||||||
|
case TYPES.VARINT:
|
||||||
|
return "varint";
|
||||||
|
case TYPES.LENDELIM:
|
||||||
|
return subType || "len_delim";
|
||||||
|
case TYPES.FIXED32:
|
||||||
|
return "fixed32";
|
||||||
|
case TYPES.FIXED64:
|
||||||
|
return "fixed64";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
24
lib/protobuf-decoder/varintUtils.js
Normal file
24
lib/protobuf-decoder/varintUtils.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"use bigint"
|
||||||
|
export function decodeVarint(buffer, offset) {
|
||||||
|
let res = BigInt(0);
|
||||||
|
let shift = 0;
|
||||||
|
let byte = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (offset >= buffer.length) {
|
||||||
|
throw new RangeError("Index out of bound decoding varint");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte = buffer[offset++];
|
||||||
|
|
||||||
|
const multiplier = exponentiate(BigInt(2), BigInt(shift));
|
||||||
|
const thisByteValue = multiply(BigInt(byte & 0x7f), multiplier);
|
||||||
|
shift += 7;
|
||||||
|
res = add(res, thisByteValue);
|
||||||
|
} while (byte >= 0x80);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: res,
|
||||||
|
length: shift / 7
|
||||||
|
};
|
||||||
|
}
|
@ -24,3 +24,23 @@ function leftpad(str, len, pad) {
|
|||||||
str
|
str
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function base64decode(base64String) {
|
||||||
|
var sliceSize = 1024;
|
||||||
|
var byteCharacters = window.atob(base64String);
|
||||||
|
var bytesLength = byteCharacters.length;
|
||||||
|
var slicesCount = Math.ceil(bytesLength / sliceSize);
|
||||||
|
var byteArrays = new Array(slicesCount);
|
||||||
|
|
||||||
|
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
|
||||||
|
var begin = sliceIndex * sliceSize;
|
||||||
|
var end = Math.min(begin + sliceSize, bytesLength);
|
||||||
|
|
||||||
|
var bytes = new Array(end - begin);
|
||||||
|
for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
|
||||||
|
bytes[i] = byteCharacters[offset].charCodeAt(0);
|
||||||
|
}
|
||||||
|
byteArrays[sliceIndex] = new Uint8Array(bytes);
|
||||||
|
}
|
||||||
|
return byteArrays
|
||||||
|
}
|
@ -1,10 +1,29 @@
|
|||||||
|
import { decodeProto } from "../../lib/protobuf-decoder/protobufDecoder";
|
||||||
import { TOTP } from "../../lib/totp-quickjs";
|
import { TOTP } from "../../lib/totp-quickjs";
|
||||||
|
import { base64decode } from "../../lib/totp-quickjs/base32decoder";
|
||||||
|
|
||||||
const otpScheme = "otpauth:/";
|
const otpauthScheme = "otpauth:/";
|
||||||
|
const googleMigrationScheme = "otpauth-migration:/";
|
||||||
|
|
||||||
export function getTOTPByLink(link) {
|
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 {
|
try {
|
||||||
let args = link.split("/", otpScheme.length);
|
let args = link.split("/", otpauthScheme.length);
|
||||||
let type = args[2]; //Returns 'hotp' or 'totp'
|
let type = args[2]; //Returns 'hotp' or 'totp'
|
||||||
let issuer = args[3].split(":")[0]?.split("?")[0]; //Returns issuer
|
let issuer = args[3].split(":")[0]?.split("?")[0]; //Returns issuer
|
||||||
let client =
|
let client =
|
||||||
@ -42,9 +61,9 @@ export function getTOTPByLink(link) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHashType(algorithm) {
|
function getByGoogleMigrationScheme(link){
|
||||||
if (algorithm == "SHA1") return "SHA-1";
|
console.log("Hello")
|
||||||
if (algorithm == "SHA256") return "SHA-256";
|
let data = link.split("data=")[1]; //Returns secret
|
||||||
if (algorithm == "SHA512") return "SHA-512";
|
let decodedProto = decodeProto(base64decode(data));
|
||||||
else return "SHA-1";
|
console.log(decodedProto)
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user