summaryrefslogtreecommitdiff
path: root/packages/anastasis-webui/src/pages/home/authMethod/totp.ts
blob: ff8027cedcdf3957cb81bbbaf5003488dd4a1699 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*
 This file is part of GNU Anastasis
 (C) 2021-2022 Anastasis SARL

 GNU Anastasis is free software; you can redistribute it and/or modify it under the
 terms of the GNU Affero General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License along with
 GNU Anastasis; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */

//@ts-ignore
import jssha from "jssha";

const SEARCH_RANGE = 16;
const timeStep = 30;

export function computeTOTPandCheck(
  secretKey: Uint8Array,
  digits: number,
  code: number,
): boolean {
  const now = new Date().getTime();
  const epoch = Math.floor(Math.round(now / 1000.0) / timeStep);

  for (let ms = -SEARCH_RANGE; ms < SEARCH_RANGE; ms++) {
    const movingFactor = (epoch + ms).toString(16).padStart(16, "0");

    const hmacSha = new jssha("SHA-1", "HEX", {
      hmacKey: { value: secretKey, format: "UINT8ARRAY" },
    });
    hmacSha.update(movingFactor);
    const hmac_text = hmacSha.getHMAC("UINT8ARRAY");

    const offset = hmac_text[hmac_text.length - 1] & 0xf;

    const otp =
      (((hmac_text[offset + 0] << 24) +
        (hmac_text[offset + 1] << 16) +
        (hmac_text[offset + 2] << 8) +
        hmac_text[offset + 3]) &
        0x7fffffff) %
      Math.pow(10, digits);

    if (otp == code) return true;
  }
  return false;
}

const encTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".split("");
export function base32enc(buffer: Uint8Array): string {
  let rpos = 0;
  let bits = 0;
  let vbit = 0;

  let result = "";
  while (rpos < buffer.length || vbit > 0) {
    if (rpos < buffer.length && vbit < 5) {
      bits = (bits << 8) | buffer[rpos++];
      vbit += 8;
    }
    if (vbit < 5) {
      bits <<= 5 - vbit;
      vbit = 5;
    }
    result += encTable__[(bits >> (vbit - 5)) & 31];
    vbit -= 5;
  }
  return result;
}

// const array = new Uint8Array(256)
// const secretKey = window.crypto.getRandomValues(array)
// console.log(base32enc(secretKey))