-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Expand file tree
/
Copy pathtotp_encryption.ts
More file actions
84 lines (67 loc) · 2.46 KB
/
totp_encryption.ts
File metadata and controls
84 lines (67 loc) · 2.46 KB
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
80
81
82
83
84
import type { OptionNames } from "@triliumnext/commons";
import optionService from "../options.js";
import { constantTimeCompare,randomSecureToken, toBase64 } from "../utils.js";
import dataEncryptionService from "./data_encryption.js";
import myScryptService from "./my_scrypt.js";
const TOTP_OPTIONS: Record<string, OptionNames> = {
SALT: "totpEncryptionSalt",
ENCRYPTED_SECRET: "totpEncryptedSecret",
VERIFICATION_HASH: "totpVerificationHash"
};
function verifyTotpSecret(secret: string): boolean {
const givenSecretHash = toBase64(myScryptService.getVerificationHash(secret));
const dbSecretHash = optionService.getOptionOrNull(TOTP_OPTIONS.VERIFICATION_HASH);
if (!dbSecretHash) {
return false;
}
return constantTimeCompare(givenSecretHash, dbSecretHash);
}
function setTotpSecret(secret: string) {
if (!secret) {
throw new Error("TOTP secret cannot be empty");
}
const encryptionSalt = randomSecureToken(32);
optionService.setOption(TOTP_OPTIONS.SALT, encryptionSalt);
const verificationHash = toBase64(myScryptService.getVerificationHash(secret));
optionService.setOption(TOTP_OPTIONS.VERIFICATION_HASH, verificationHash);
const encryptedSecret = dataEncryptionService.encrypt(
Buffer.from(encryptionSalt),
secret
);
optionService.setOption(TOTP_OPTIONS.ENCRYPTED_SECRET, encryptedSecret);
}
function getTotpSecret(): string | null {
const encryptionSalt = optionService.getOptionOrNull(TOTP_OPTIONS.SALT);
const encryptedSecret = optionService.getOptionOrNull(TOTP_OPTIONS.ENCRYPTED_SECRET);
if (!encryptionSalt || !encryptedSecret) {
return null;
}
try {
const decryptedSecret = dataEncryptionService.decrypt(
Buffer.from(encryptionSalt),
encryptedSecret
);
if (!decryptedSecret) {
return null;
}
return decryptedSecret.toString();
} catch (e) {
console.error("Failed to decrypt TOTP secret:", e);
return null;
}
}
function resetTotpSecret() {
optionService.setOption(TOTP_OPTIONS.SALT, "");
optionService.setOption(TOTP_OPTIONS.ENCRYPTED_SECRET, "");
optionService.setOption(TOTP_OPTIONS.VERIFICATION_HASH, "");
}
function isTotpSecretSet(): boolean {
return !!optionService.getOptionOrNull(TOTP_OPTIONS.VERIFICATION_HASH);
}
export default {
verifyTotpSecret,
setTotpSecret,
getTotpSecret,
resetTotpSecret,
isTotpSecretSet
};