| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667 |
- import base32Decode from 'base32-decode';
- import crypto from 'crypto';
- const RFC_4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
- export class TotpChecker {
- static ValidateTotp(totpSecret, code, period, digits, algorithm) {
- if (!totpSecret && !code)
- return true;
- if ((!totpSecret && code) || (totpSecret && !code))
- return false;
- let currentPeriod = TotpChecker.getCurrentPeriod(period);
- const input = Buffer.from(code.replace(/[^0-9]/g, "").trim());
- return TotpChecker.DoGenerateCode(totpSecret, [currentPeriod - 1, currentPeriod, currentPeriod + 1], digits || 6, algorithm || "SHA-1")
- .find(x => {
- try {
- return crypto.timingSafeEqual(Buffer.from(x), input);
- }
- catch (err) {
- return false;
- }
- }) !== undefined;
- }
- static DoGenerateCode(totpSecret, period, digits, algorithm) {
- const secretAsBase64 = Buffer.from(base32Decode(totpSecret, "RFC4648"));
- return (Array.isArray(period) ? period : [period]).map(i => {
- // Encode period as a Buffer
- var periodAsBuffer = Buffer.alloc(8);
- periodAsBuffer.write((i.toString(16)).padStart(16, '0'), 0, 'hex');
- return periodAsBuffer;
- }).map(period => {
- // Encode period using algorithm and secret
- return crypto.createHmac(algorithm, secretAsBase64)
- .update(period)
- .digest();
- }).map(hash => {
- // Truncate output hash
- let offset = hash[hash.length - 1] & 0xF;
- var truncatedHash = ((hash[offset + 0] & 0x7F) << 24 |
- (hash[offset + 1] & 0xFF) << 16 |
- (hash[offset + 2] & 0xFF) << 8 |
- (hash[offset + 3] & 0xFF)) % (10 ** digits);
- return (`${truncatedHash}`.padStart(digits, '0'));
- });
- }
- static getCurrentPeriod(period) {
- return Math.floor(Date.now() / ((period || 30) * 1000));
- }
- static GenerateCode(totpSecret, period, digits, algorithm) {
- return TotpChecker.DoGenerateCode(totpSecret, this.getCurrentPeriod(period), digits || 6, algorithm || "SHA-1").shift();
- }
- static GenerateSecret(secretLength) {
- secretLength = secretLength || 13;
- return Array.from(crypto.randomBytes(secretLength)).map(x => RFC_4648[x % RFC_4648.length]).join("");
- }
- static GenerateUrl(optionsOrIssuer) {
- let options = typeof optionsOrIssuer === "string" ? { issuer: optionsOrIssuer } : optionsOrIssuer;
- options.digits = options.digits || 6;
- options.period = options.period || 30;
- options.algorithm = options.algorithm || "SHA-1";
- options.label = encodeURIComponent(options.label || options.issuer);
- const secretStr = options.secret || TotpChecker.GenerateSecret(options.secretLength || 13);
- return {
- url: `otpauth://totp/${options.issuer}?issuer=${options.issuer}&secret=${secretStr}&digits=${options.digits}&period=${options.period}&algorithm=${options.algorithm}`,
- secret: secretStr
- };
- }
- }
- //# sourceMappingURL=totpChecker.js.map
|