import sjcl from "../internal/sjcl.js" ; import { bitArrayToUint8Array , uint8ArrayToBitArray } from "./Utils.js" ; import { hexToUint8Array } from "@tutao/tutanota-utils" ; import { random } from "../random/Randomizer.js" ; export let DIGITS = 6 ; export type Base32 = string ; const DIGITS_POWER = [ 1 , 10 , 100 , 1000 , 10000 , 100000 , 1000000 , 10000000 , 100000000 ] ; const base32 = sjcl . codec . base32 ; export type TotpSecret = { key : Uint8Array ; readableKey : Base32 ; } ; export class TotpVerifier { _digits : number ; constructor ( digits = DIGITS ) { this . _digits = digits ; } generateSecret ( ) { let key = random . generateRandomData ( 16 ) ; let readableKey = this . readableKey ( key ) ; return { key , readableKey , } ; } param time param key return generateTotp ( time , key ) { let timeHex = time . toString ( 16 ) ; while ( timeHex . length < 16 ) timeHex = "0" + timeHex ; let msg = hexToUint8Array ( timeHex ) ; let hash = this . hmac_sha ( key , msg ) ; let offset = hash [ hash . length - 1 ] & 0xf ; let binary = ( ( hash [ offset ] & 0x7f ) << 24 ) | ( ( hash [ offset + 1 ] & 0xff ) << 16 ) | ( ( hash [ offset + 2 ] & 0xff ) << 8 ) | ( hash [ offset + 3 ] & 0xff ) ; let code = binary % DIGITS_POWER [ this . _digits ] ; return code ; } hmac_sha ( key , text ) { let hmac = new sjcl . misc . hmac ( uint8ArrayToBitArray ( key ) , sjcl . hash . sha1 ) ; return bitArrayToUint8Array ( hmac . encrypt ( uint8ArrayToBitArray ( text ) ) ) ; } readableKey ( key ) { return base32 . fromBits ( uint8ArrayToBitArray ( key ) ) . toLowerCase ( ) . replace ( /(.{4})/g , "$1 " ) . replace ( /=/g , "" ) . trim ( ) ; } }