Add missing modules

This commit is contained in:
Martin Kleinschrodt 2019-05-28 17:22:08 +02:00
parent 74c3baff53
commit 96613afb23
2 changed files with 105 additions and 0 deletions

View File

@ -0,0 +1,52 @@
import { Err, ErrorCode } from "./error";
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
export function bytesToBase32(arr: Uint8Array) {
let bits = 0;
let value = 0;
let str = "";
for (let i = 0; i < arr.length; i++) {
value = (value << 8) | arr[i];
bits += 8;
while (bits >= 5) {
str += chars[(value >>> (bits - 5)) & 31];
bits -= 5;
}
}
if (bits > 0) {
str += chars[(value << (5 - bits)) & 31];
}
return str;
}
export function base32ToBytes(str: string) {
const strUpp = str.toUpperCase();
const arr = new Uint8Array(((str.length * 5) / 8) | 0);
let bits = 0;
let value = 0;
let index = 0;
for (let i = 0; i < strUpp.length; i++) {
const idx = chars.indexOf(strUpp[i]);
if (idx === -1) {
throw new Err(ErrorCode.ENCODING_ERROR, `Invalid Base32 character found: ${strUpp[i]}`);
}
value = (value << 5) | idx;
bits += 5;
if (bits >= 8) {
arr[index++] = (value >>> (bits - 8)) & 255;
bits -= 8;
}
}
return arr;
}

53
packages/core/src/otp.ts Normal file
View File

@ -0,0 +1,53 @@
import { numToBytes, bytesToNum } from "./encoding";
import { getProvider, HMACParams } from "./crypto";
import { base32ToBytes } from "./encoding";
export interface HOTPOpts {
digits: number;
hash: "SHA-1" | "SHA-256";
}
export interface TOTPOpts extends HOTPOpts {
interval: number;
}
function getToken(hmac: Uint8Array, digits: number = 6): string {
const offset = hmac[hmac.length - 1] & 0xf;
const bin = new Uint8Array([hmac[offset] & 0x7f, hmac[offset + 1], hmac[offset + 2], hmac[offset + 3]]);
const num = bytesToNum(bin);
return (num % 10 ** digits).toString().padStart(digits, "0");
}
export async function hotp(
secret: Uint8Array,
counter: number,
{ hash, digits }: HOTPOpts = { digits: 6, hash: "SHA-1" }
) {
const hmac = await getProvider().sign(
secret,
numToBytes(counter),
new HMACParams({ hash, keySize: secret.length * 8 })
);
return getToken(hmac, digits);
}
export async function totp(
secret: Uint8Array,
time: number = Date.now(),
{ interval, ...opts }: TOTPOpts = { interval: 30, digits: 6, hash: "SHA-1" }
) {
const counter = Math.floor(time / interval / 1000);
return hotp(secret, counter, opts);
}
export function parseURL(data: string) {
const url = new URL(data);
const params = new URLSearchParams(url.search);
const secret = params.get("secret");
if (!secret || !base32ToBytes(secret).length) {
throw "Invalid secret";
}
return { secret };
}