mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-09-08 08:41:37 +08:00
90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
![]() |
'use strict';
|
||
|
|
||
|
import sql from '../sql.js';
|
||
|
import optionService from '../options.js';
|
||
|
import crypto from 'crypto';
|
||
|
|
||
|
function isRecoveryCodeSet() {
|
||
|
return optionService.getOptionBool('encryptedRecoveryCodes');
|
||
|
}
|
||
|
|
||
|
function setRecoveryCodes(recoveryCodes: string) {
|
||
|
const iv = crypto.randomBytes(16);
|
||
|
const securityKey = crypto.randomBytes(32);
|
||
|
const cipher = crypto.createCipheriv('aes-256-cbc', securityKey, iv);
|
||
|
let encryptedRecoveryCodes = cipher.update(recoveryCodes, 'utf-8', 'hex');
|
||
|
|
||
|
sql.transactional(() => {
|
||
|
optionService.setOption('recoveryCodeInitialVector', iv.toString('hex'));
|
||
|
optionService.setOption('recoveryCodeSecurityKey', securityKey.toString('hex'));
|
||
|
optionService.setOption('recoveryCodesEncrypted', encryptedRecoveryCodes + cipher.final('hex'));
|
||
|
optionService.setOption('encryptedRecoveryCodes', 'true');
|
||
|
return true;
|
||
|
});
|
||
|
return false;
|
||
|
}
|
||
|
function getRecoveryCodes() {
|
||
|
if (!isRecoveryCodeSet()) {
|
||
|
return Array(8).fill("Keys not set")
|
||
|
}
|
||
|
|
||
|
return sql.transactional(() => {
|
||
|
const iv = Buffer.from(optionService.getOption('recoveryCodeInitialVector'), 'hex');
|
||
|
const securityKey = Buffer.from(optionService.getOption('recoveryCodeSecurityKey'), 'hex');
|
||
|
const encryptedRecoveryCodes = optionService.getOption('recoveryCodesEncrypted');
|
||
|
|
||
|
const decipher = crypto.createDecipheriv('aes-256-cbc', securityKey, iv);
|
||
|
const decryptedData = decipher.update(encryptedRecoveryCodes, 'hex', 'utf-8');
|
||
|
|
||
|
const decryptedString = decryptedData + decipher.final('utf-8');
|
||
|
return decryptedString.split(',');
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function removeRecoveryCode(usedCode: string) {
|
||
|
const oldCodes: string[] = getRecoveryCodes();
|
||
|
const today = new Date();
|
||
|
oldCodes[oldCodes.indexOf(usedCode)] = today.toJSON().replace(/-/g, '/');
|
||
|
setRecoveryCodes(oldCodes.toString());
|
||
|
}
|
||
|
|
||
|
function verifyRecoveryCode(recoveryCodeGuess: string) {
|
||
|
const recoveryCodeRegex = RegExp(/^.{22}==$/gm);
|
||
|
if (!recoveryCodeRegex.test(recoveryCodeGuess)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const recoveryCodes = getRecoveryCodes();
|
||
|
var loginSuccess = false;
|
||
|
recoveryCodes.forEach((recoveryCode: string) => {
|
||
|
if (recoveryCodeGuess === recoveryCode) {
|
||
|
removeRecoveryCode(recoveryCode);
|
||
|
loginSuccess = true;
|
||
|
return;
|
||
|
}
|
||
|
});
|
||
|
return loginSuccess;
|
||
|
}
|
||
|
|
||
|
function getUsedRecoveryCodes() {
|
||
|
if (!isRecoveryCodeSet()){
|
||
|
return Array(8).fill("Recovery code not set")
|
||
|
}
|
||
|
|
||
|
const dateRegex = RegExp(/^\d{4}\/\d{2}\/\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/gm);
|
||
|
const recoveryCodes = getRecoveryCodes();
|
||
|
const usedStatus: string[] = [];
|
||
|
|
||
|
recoveryCodes.forEach((recoveryKey: string) => {
|
||
|
if (dateRegex.test(recoveryKey)) usedStatus.push('Used: ' + recoveryKey);
|
||
|
else usedStatus.push('Recovery code ' + recoveryCodes.indexOf(recoveryKey) + ' is unused');
|
||
|
});
|
||
|
return usedStatus;
|
||
|
}
|
||
|
|
||
|
export default {
|
||
|
setRecoveryCodes,
|
||
|
verifyRecoveryCode,
|
||
|
getUsedRecoveryCodes,
|
||
|
isRecoveryCodeSet
|
||
|
};
|