talon-one/.obsidian/plugins/meld-encrypt/main.js

765 lines
126 KiB
JavaScript
Raw Normal View History

2022-06-27 08:33:08 +00:00
'use strict';
var obsidian = require('obsidian');
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class DecryptModal extends obsidian.Modal {
constructor(app, title, text = '', showButton) {
super(app);
this.decryptInPlace = false;
this.text = text;
this.titleEl.innerText = title;
this.showButton = showButton;
}
onOpen() {
let { contentEl } = this;
const textEl = contentEl.createDiv().createEl('textarea', { text: this.text });
textEl.style.width = '100%';
textEl.style.height = '100%';
textEl.rows = 10;
textEl.readOnly = true;
//textEl.focus(); // Doesn't seem to work here...
setTimeout(() => { textEl.focus(); }, 100); //... but this does
const btnContainerEl = contentEl.createDiv('');
if (this.showButton) {
const copyBtnEl = btnContainerEl.createEl('button', { text: 'Copy' });
copyBtnEl.addEventListener('click', () => {
navigator.clipboard.writeText(textEl.value);
});
}
const decryptInPlaceBtnEl = btnContainerEl.createEl('button', { text: 'Decrypt in-place' });
decryptInPlaceBtnEl.addEventListener('click', () => {
this.decryptInPlace = true;
this.close();
});
const cancelBtnEl = btnContainerEl.createEl('button', { text: 'Close' });
cancelBtnEl.addEventListener('click', () => {
this.close();
});
}
}
class PasswordModal extends obsidian.Modal {
constructor(app, isEncrypting, confirmPassword, defaultPassword = null, hint) {
super(app);
this.password = null;
this.hint = null;
this.defaultPassword = null;
this.defaultPassword = defaultPassword;
this.confirmPassword = confirmPassword;
this.isEncrypting = isEncrypting;
this.hint = hint;
}
onOpen() {
var _a, _b, _c;
let { contentEl } = this;
contentEl.empty();
contentEl.addClass('meld-e-password');
if (obsidian.Platform.isMobile) {
contentEl.addClass('meld-e-platform-mobile');
}
else if (obsidian.Platform.isDesktop) {
contentEl.addClass('meld-e-platform-desktop');
}
/* Main password input row */
const inputPwContainerEl = contentEl.createDiv({ cls: 'meld-e-row' });
inputPwContainerEl.createSpan({ cls: 'meld-e-icon', text: '🔑' });
const pwInputEl = inputPwContainerEl.createEl('input', { type: 'password', value: (_a = this.defaultPassword) !== null && _a !== void 0 ? _a : '' });
pwInputEl.placeholder = 'Enter your password';
pwInputEl.focus();
if (obsidian.Platform.isMobile) {
// Add 'Next' button for mobile
const inputInputNextBtnEl = inputPwContainerEl.createEl('button', {
text: '→',
cls: 'meld-e-button-next'
});
inputInputNextBtnEl.addEventListener('click', (ev) => {
inputPasswordHandler();
});
}
/* End Main password input row */
/* Confirm password input row */
const confirmPwShown = this.confirmPassword;
const confirmPwContainerEl = contentEl.createDiv({ cls: 'meld-e-row' });
confirmPwContainerEl.createSpan({ cls: 'meld-e-icon', text: '🔑' });
const pwConfirmInputEl = confirmPwContainerEl.createEl('input', {
type: 'password',
value: (_b = this.defaultPassword) !== null && _b !== void 0 ? _b : ''
});
pwConfirmInputEl.placeholder = 'Confirm your password';
const messageEl = contentEl.createDiv({ cls: 'meld-e-message' });
messageEl.hide();
if (obsidian.Platform.isMobile) {
// Add 'Next' button for mobile
const confirmInputNextBtnEl = confirmPwContainerEl.createEl('button', {
text: '→',
cls: 'meld-e-button-next'
});
confirmInputNextBtnEl.addEventListener('click', (ev) => {
confirmPasswordHandler();
});
}
if (!confirmPwShown) {
confirmPwContainerEl.hide();
}
/* End Confirm password input row */
/* Hint input row */
const hintInputShown = this.isEncrypting;
const inputHintContainerEl = contentEl.createDiv({ cls: 'meld-e-row' });
inputHintContainerEl.createSpan({ cls: 'meld-e-icon', text: '💡' });
const hintInputEl = inputHintContainerEl.createEl('input', { type: 'text', value: this.hint });
hintInputEl.placeholder = 'Enter an optional password hint';
if (obsidian.Platform.isMobile) {
// Add 'Next' button for mobile
const hintInputNextBtnEl = inputHintContainerEl.createEl('button', {
text: '→',
cls: 'meld-e-button-next'
});
hintInputNextBtnEl.addEventListener('click', (ev) => {
hintPasswordHandler();
});
}
if (!hintInputShown) {
inputHintContainerEl.hide();
}
/* End Hint input row */
/* Hint text row */
const spanHintContainerEl = contentEl.createDiv({ cls: 'meld-e-row' });
spanHintContainerEl.createSpan({ cls: 'meld-e-icon', text: '💡' });
spanHintContainerEl.createSpan({ cls: 'meld-e-hint', text: `Hint: '${this.hint}'` });
if (hintInputShown || ((_c = this.hint) !== null && _c !== void 0 ? _c : '').length == 0) {
spanHintContainerEl.hide();
}
/* END Hint text row */
const confirmPwButtonEl = contentEl.createEl('button', {
text: 'Confirm',
cls: 'meld-e-button-confirm'
});
confirmPwButtonEl.addEventListener('click', (ev) => {
if (validate()) {
this.close();
}
else {
pwInputEl.focus();
}
});
const validate = () => {
if (confirmPwShown) {
if (pwInputEl.value != pwConfirmInputEl.value) {
// passwords don't match
messageEl.setText('Passwords don\'t match');
messageEl.show();
return false;
}
}
this.password = pwInputEl.value;
this.hint = hintInputEl.value;
return true;
};
const inputPasswordHandler = () => {
if (confirmPwShown) {
pwConfirmInputEl.focus();
return;
}
if (hintInputShown) {
hintInputEl.focus();
return;
}
if (validate()) {
this.close();
}
};
const confirmPasswordHandler = () => {
if (validate()) {
if (hintInputShown) {
hintInputEl.focus();
}
else {
this.close();
}
}
};
const hintPasswordHandler = () => {
if (validate()) {
this.close();
}
else {
pwInputEl.focus();
}
};
hintInputEl.addEventListener('keypress', (ev) => {
if ((ev.code === 'Enter' || ev.code === 'NumpadEnter')
&& pwInputEl.value.length > 0) {
ev.preventDefault();
hintPasswordHandler();
}
});
pwConfirmInputEl.addEventListener('keypress', (ev) => {
if ((ev.code === 'Enter' || ev.code === 'NumpadEnter')
&& pwConfirmInputEl.value.length > 0) {
ev.preventDefault();
confirmPasswordHandler();
}
});
pwInputEl.addEventListener('keypress', (ev) => {
if ((ev.code === 'Enter' || ev.code === 'NumpadEnter')
&& pwInputEl.value.length > 0) {
ev.preventDefault();
inputPasswordHandler();
}
});
}
}
const vectorSize = 16;
const utf8Encoder = new TextEncoder();
const utf8Decoder = new TextDecoder();
const iterations = 1000;
const salt = utf8Encoder.encode('XHWnDAT6ehMVY2zD');
class CryptoHelperV2 {
deriveKey(password) {
return __awaiter(this, void 0, void 0, function* () {
const buffer = utf8Encoder.encode(password);
const key = yield crypto.subtle.importKey('raw', buffer, { name: 'PBKDF2' }, false, ['deriveKey']);
const privateKey = crypto.subtle.deriveKey({
name: 'PBKDF2',
hash: { name: 'SHA-256' },
iterations,
salt
}, key, {
name: 'AES-GCM',
length: 256
}, false, ['encrypt', 'decrypt']);
return privateKey;
});
}
encryptToBase64(text, password) {
return __awaiter(this, void 0, void 0, function* () {
const key = yield this.deriveKey(password);
const textBytesToEncrypt = utf8Encoder.encode(text);
const vector = crypto.getRandomValues(new Uint8Array(vectorSize));
// encrypt into bytes
const encryptedBytes = new Uint8Array(yield crypto.subtle.encrypt({ name: 'AES-GCM', iv: vector }, key, textBytesToEncrypt));
const finalBytes = new Uint8Array(vector.byteLength + encryptedBytes.byteLength);
finalBytes.set(vector, 0);
finalBytes.set(encryptedBytes, vector.byteLength);
//convert array to base64
const base64Text = btoa(String.fromCharCode(...finalBytes));
return base64Text;
});
}
stringToArray(str) {
var result = [];
for (var i = 0; i < str.length; i++) {
result.push(str.charCodeAt(i));
}
return new Uint8Array(result);
}
decryptFromBase64(base64Encoded, password) {
return __awaiter(this, void 0, void 0, function* () {
try {
let bytesToDecode = this.stringToArray(atob(base64Encoded));
// extract iv
const vector = bytesToDecode.slice(0, vectorSize);
// extract encrypted text
const encryptedTextBytes = bytesToDecode.slice(vectorSize);
const key = yield this.deriveKey(password);
// decrypt into bytes
let decryptedBytes = yield crypto.subtle.decrypt({ name: 'AES-GCM', iv: vector }, key, encryptedTextBytes);
// convert bytes to text
let decryptedText = utf8Decoder.decode(decryptedBytes);
return decryptedText;
}
catch (e) {
//console.error(e);
return null;
}
});
}
}
const algorithmObsolete = {
name: 'AES-GCM',
iv: new Uint8Array([196, 190, 240, 190, 188, 78, 41, 132, 15, 220, 84, 211]),
tagLength: 128
};
class CryptoHelperObsolete {
buildKey(password) {
return __awaiter(this, void 0, void 0, function* () {
let utf8Encode = new TextEncoder();
let passwordBytes = utf8Encode.encode(password);
let passwordDigest = yield crypto.subtle.digest({ name: 'SHA-256' }, passwordBytes);
let key = yield crypto.subtle.importKey('raw', passwordDigest, algorithmObsolete, false, ['encrypt', 'decrypt']);
return key;
});
}
encryptToBase64(text, password) {
return __awaiter(this, void 0, void 0, function* () {
let key = yield this.buildKey(password);
let utf8Encode = new TextEncoder();
let bytesToEncrypt = utf8Encode.encode(text);
// encrypt into bytes
let encryptedBytes = new Uint8Array(yield crypto.subtle.encrypt(algorithmObsolete, key, bytesToEncrypt));
//convert array to base64
let base64Text = btoa(String.fromCharCode(...encryptedBytes));
return base64Text;
});
}
stringToArray(str) {
var result = [];
for (var i = 0; i < str.length; i++) {
result.push(str.charCodeAt(i));
}
return new Uint8Array(result);
}
decryptFromBase64(base64Encoded, password) {
return __awaiter(this, void 0, void 0, function* () {
try {
// convert base 64 to array
let bytesToDecrypt = this.stringToArray(atob(base64Encoded));
let key = yield this.buildKey(password);
// decrypt into bytes
let decryptedBytes = yield crypto.subtle.decrypt(algorithmObsolete, key, bytesToDecrypt);
// convert bytes to text
let utf8Decode = new TextDecoder();
let decryptedText = utf8Decode.decode(decryptedBytes);
return decryptedText;
}
catch (e) {
return null;
}
});
}
}
class MeldEncryptSettingsTab extends obsidian.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
let { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'Settings for Meld Encrypt' });
new obsidian.Setting(containerEl)
.setName('Expand selection to whole line?')
.setDesc('Partial selections will get expanded to the whole line.')
.addToggle(toggle => {
toggle
.setValue(this.plugin.settings.expandToWholeLines)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.expandToWholeLines = value;
yield this.plugin.saveSettings();
//this.updateSettingsUi();
}));
});
new obsidian.Setting(containerEl)
.setName('Confirm password?')
.setDesc('Confirm password when encrypting.')
.addToggle(toggle => {
toggle
.setValue(this.plugin.settings.confirmPassword)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.confirmPassword = value;
yield this.plugin.saveSettings();
this.updateSettingsUi();
}));
});
new obsidian.Setting(containerEl)
.setName('Copy button?')
.setDesc('Show a button to copy decrypted text.')
.addToggle(toggle => {
toggle
.setValue(this.plugin.settings.showButton)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.showButton = value;
yield this.plugin.saveSettings();
this.updateSettingsUi();
}));
});
new obsidian.Setting(containerEl)
.setName('Remember password?')
.setDesc('Remember the last used password for this session.')
.addToggle(toggle => {
toggle
.setValue(this.plugin.settings.rememberPassword)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.rememberPassword = value;
yield this.plugin.saveSettings();
this.updateSettingsUi();
}));
});
this.pwTimeoutSetting = new obsidian.Setting(containerEl)
.setName(this.buildPasswordTimeoutSettingName())
.setDesc('The number of minutes to remember the last used password.')
.addSlider(slider => {
slider
.setLimits(0, 120, 5)
.setValue(this.plugin.settings.rememberPasswordTimeout)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.rememberPasswordTimeout = value;
yield this.plugin.saveSettings();
this.updateSettingsUi();
}));
});
this.updateSettingsUi();
}
updateSettingsUi() {
this.pwTimeoutSetting.setName(this.buildPasswordTimeoutSettingName());
if (this.plugin.settings.rememberPassword) {
this.pwTimeoutSetting.settingEl.show();
}
else {
this.pwTimeoutSetting.settingEl.hide();
}
}
buildPasswordTimeoutSettingName() {
const value = this.plugin.settings.rememberPasswordTimeout;
let timeoutString = `${value} minutes`;
if (value == 0) {
timeoutString = 'Never forget';
}
return `Remember Password Timeout (${timeoutString})`;
}
}
const _PREFIX = '%%🔐';
const _PREFIX_OBSOLETE = _PREFIX + ' ';
const _PREFIX_A = _PREFIX + 'α ';
const _SUFFIX = ' 🔐%%';
const _HINT = '💡';
const DEFAULT_SETTINGS = {
expandToWholeLines: true,
confirmPassword: true,
showButton: false,
rememberPassword: true,
rememberPasswordTimeout: 30
};
class MeldEncrypt extends obsidian.Plugin {
onload() {
return __awaiter(this, void 0, void 0, function* () {
yield this.loadSettings();
this.addSettingTab(new MeldEncryptSettingsTab(this.app, this));
this.addCommand({
id: 'meld-encrypt',
name: 'Encrypt/Decrypt',
editorCheckCallback: (checking, editor, view) => this.processEncryptDecryptCommand(checking, editor, view, false)
});
this.addCommand({
id: 'meld-encrypt-in-place',
name: 'Encrypt/Decrypt In-place',
editorCheckCallback: (checking, editor, view) => this.processEncryptDecryptCommand(checking, editor, view, true)
});
this.addCommand({
id: 'meld-encrypt-note',
name: 'Encrypt/Decrypt Whole Note',
editorCheckCallback: (checking, editor, view) => this.processEncryptDecryptWholeNoteCommand(checking, editor, view)
});
});
}
loadSettings() {
return __awaiter(this, void 0, void 0, function* () {
this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
});
}
saveSettings() {
return __awaiter(this, void 0, void 0, function* () {
yield this.saveData(this.settings);
});
}
isSettingsModalOpen() {
return document.querySelector('.mod-settings') !== null;
}
processEncryptDecryptWholeNoteCommand(checking, editor, view) {
if (checking && this.isSettingsModalOpen()) {
// Settings is open, ensures this command can show up in other
// plugins which list commands e.g. customizable-sidebar
return true;
}
const startPos = editor.offsetToPos(0);
const endPos = { line: editor.lastLine(), ch: editor.getLine(editor.lastLine()).length };
const selectionText = editor.getRange(startPos, endPos).trim();
return this.processSelection(checking, editor, selectionText, startPos, endPos, true);
}
processEncryptDecryptCommand(checking, editor, view, decryptInPlace) {
if (checking && this.isSettingsModalOpen()) {
// Settings is open, ensures this command can show up in other
// plugins which list commands e.g. customizable-sidebar
return true;
}
let startPos = editor.getCursor('from');
let endPos = editor.getCursor('to');
if (this.settings.expandToWholeLines) {
const startLine = startPos.line;
startPos = { line: startLine, ch: 0 }; // want the start of the first line
const endLine = endPos.line;
const endLineText = editor.getLine(endLine);
endPos = { line: endLine, ch: endLineText.length }; // want the end of last line
}
else {
if (!editor.somethingSelected()) {
// nothing selected, assume user wants to decrypt, expand to start and end markers
startPos = this.getClosestPrevTextCursorPos(editor, _PREFIX, startPos);
endPos = this.getClosestNextTextCursorPos(editor, _SUFFIX, endPos);
}
}
const selectionText = editor.getRange(startPos, endPos);
return this.processSelection(checking, editor, selectionText, startPos, endPos, decryptInPlace);
}
getClosestPrevTextCursorPos(editor, text, defaultValue) {
const initOffset = editor.posToOffset(editor.getCursor("from"));
for (let offset = initOffset; offset >= 0; offset--) {
const offsetPos = editor.offsetToPos(offset);
const textEndOffset = offset + text.length;
const prefixEndPos = editor.offsetToPos(textEndOffset);
const testText = editor.getRange(offsetPos, prefixEndPos);
if (testText == text) {
return offsetPos;
}
}
return defaultValue;
}
getClosestNextTextCursorPos(editor, text, defaultValue) {
const initOffset = editor.posToOffset(editor.getCursor("from"));
const lastLineNum = editor.lastLine();
let maxOffset = editor.posToOffset({ line: lastLineNum, ch: editor.getLine(lastLineNum).length });
for (let offset = initOffset; offset <= maxOffset - text.length; offset++) {
const offsetPos = editor.offsetToPos(offset);
const textEndOffset = offset + text.length;
const prefixEndPos = editor.offsetToPos(textEndOffset);
const testText = editor.getRange(offsetPos, prefixEndPos);
if (testText == text) {
return prefixEndPos;
}
}
return defaultValue;
}
analyseSelection(selectionText) {
const result = new SelectionAnalysis();
result.isEmpty = selectionText.length === 0;
result.hasObsoleteEncryptedPrefix = selectionText.startsWith(_PREFIX_OBSOLETE);
result.hasEncryptedPrefix = result.hasObsoleteEncryptedPrefix || selectionText.startsWith(_PREFIX_A);
result.hasDecryptSuffix = selectionText.endsWith(_SUFFIX);
result.containsEncryptedMarkers =
selectionText.contains(_PREFIX_OBSOLETE)
|| selectionText.contains(_PREFIX_A)
|| selectionText.contains(_SUFFIX);
result.canDecrypt = result.hasEncryptedPrefix && result.hasDecryptSuffix;
result.canEncrypt = !result.hasEncryptedPrefix && !result.containsEncryptedMarkers;
if (result.canDecrypt) {
result.decryptable = this.parseDecryptableContent(selectionText);
if (result.decryptable == null) {
result.canDecrypt = false;
}
}
return result;
}
processSelection(checking, editor, selectionText, finalSelectionStart, finalSelectionEnd, decryptInPlace) {
var _a;
const selectionAnalysis = this.analyseSelection(selectionText);
if (selectionAnalysis.isEmpty) {
if (!checking) {
new obsidian.Notice('Nothing to Encrypt.');
}
return false;
}
if (!selectionAnalysis.canDecrypt && !selectionAnalysis.canEncrypt) {
if (!checking) {
new obsidian.Notice('Unable to Encrypt or Decrypt that.');
}
return false;
}
if (checking) {
return true;
}
// Fetch password from user
// determine default password
const isRememberPasswordExpired = !this.settings.rememberPassword
|| (this.passwordLastUsedExpiry != null
&& Date.now() > this.passwordLastUsedExpiry);
const confirmPassword = selectionAnalysis.canEncrypt && this.settings.confirmPassword;
if (isRememberPasswordExpired || confirmPassword) {
// forget password
this.passwordLastUsed = '';
}
const pwModal = new PasswordModal(this.app, selectionAnalysis.canEncrypt, confirmPassword, this.passwordLastUsed, (_a = selectionAnalysis.decryptable) === null || _a === void 0 ? void 0 : _a.hint);
pwModal.onClose = () => {
var _a;
const pw = (_a = pwModal.password) !== null && _a !== void 0 ? _a : '';
if (pw.length == 0) {
return;
}
const hint = pwModal.hint;
// remember password?
if (this.settings.rememberPassword) {
this.passwordLastUsed = pw;
this.passwordLastUsedExpiry =
this.settings.rememberPasswordTimeout == 0
? null
: Date.now() + this.settings.rememberPasswordTimeout * 1000 * 60 // new expiry
;
}
if (selectionAnalysis.canEncrypt) {
const encryptable = new Encryptable();
encryptable.text = selectionText;
encryptable.hint = hint;
this.encryptSelection(editor, encryptable, pw, finalSelectionStart, finalSelectionEnd);
}
else {
if (selectionAnalysis.decryptable.version == 1) {
this.decryptSelection_a(editor, selectionAnalysis.decryptable, pw, finalSelectionStart, finalSelectionEnd, decryptInPlace);
}
else {
this.decryptSelectionObsolete(editor, selectionAnalysis.decryptable, pw, finalSelectionStart, finalSelectionEnd, decryptInPlace);
}
}
};
pwModal.open();
return true;
}
encryptSelection(editor, encryptable, password, finalSelectionStart, finalSelectionEnd) {
return __awaiter(this, void 0, void 0, function* () {
//encrypt
const crypto = new CryptoHelperV2();
const encodedText = this.encodeEncryption(yield crypto.encryptToBase64(encryptable.text, password), encryptable.hint);
editor.setSelection(finalSelectionStart, finalSelectionEnd);
editor.replaceSelection(encodedText);
});
}
decryptSelection_a(editor, decryptable, password, selectionStart, selectionEnd, decryptInPlace) {
return __awaiter(this, void 0, void 0, function* () {
// decrypt
const crypto = new CryptoHelperV2();
const decryptedText = yield crypto.decryptFromBase64(decryptable.base64CipherText, password);
if (decryptedText === null) {
new obsidian.Notice('❌ Decryption failed!');
}
else {
if (decryptInPlace) {
editor.setSelection(selectionStart, selectionEnd);
editor.replaceSelection(decryptedText);
}
else {
const decryptModal = new DecryptModal(this.app, '🔓', decryptedText, this.settings.showButton);
decryptModal.onClose = () => {
editor.focus();
if (decryptModal.decryptInPlace) {
editor.setSelection(selectionStart, selectionEnd);
editor.replaceSelection(decryptedText);
}
};
decryptModal.open();
}
}
});
}
decryptSelectionObsolete(editor, decryptable, password, selectionStart, selectionEnd, decryptInPlace) {
return __awaiter(this, void 0, void 0, function* () {
// decrypt
const base64CipherText = this.removeMarkers(decryptable.base64CipherText);
const crypto = new CryptoHelperObsolete();
const decryptedText = yield crypto.decryptFromBase64(base64CipherText, password);
if (decryptedText === null) {
new obsidian.Notice('❌ Decryption failed!');
}
else {
if (decryptInPlace) {
editor.setSelection(selectionStart, selectionEnd);
editor.replaceSelection(decryptedText);
}
else {
const decryptModal = new DecryptModal(this.app, '🔓', decryptedText, this.settings.showButton);
decryptModal.onClose = () => {
editor.focus();
if (decryptModal.decryptInPlace) {
editor.setSelection(selectionStart, selectionEnd);
editor.replaceSelection(decryptedText);
}
};
decryptModal.open();
}
}
});
}
parseDecryptableContent(text) {
const result = new Decryptable();
let content = text;
if (content.startsWith(_PREFIX_A) && content.endsWith(_SUFFIX)) {
result.version = 1;
content = content.replace(_PREFIX_A, '').replace(_SUFFIX, '');
}
else if (content.startsWith(_PREFIX_OBSOLETE) && content.endsWith(_SUFFIX)) {
result.version = 0;
content = content.replace(_PREFIX_OBSOLETE, '').replace(_SUFFIX, '');
}
else {
return null; // invalid format
}
// check if there is a hint
//console.table(content);
if (content.substr(0, _HINT.length) == _HINT) {
const endHintMarker = content.indexOf(_HINT, _HINT.length);
if (endHintMarker < 0) {
return null; // invalid format
}
result.hint = content.substring(_HINT.length, endHintMarker);
result.base64CipherText = content.substring(endHintMarker + _HINT.length);
}
else {
result.base64CipherText = content;
}
//console.table(result);
return result;
}
removeMarkers(text) {
if (text.startsWith(_PREFIX_A) && text.endsWith(_SUFFIX)) {
return text.replace(_PREFIX_A, '').replace(_SUFFIX, '');
}
if (text.startsWith(_PREFIX_OBSOLETE) && text.endsWith(_SUFFIX)) {
return text.replace(_PREFIX_OBSOLETE, '').replace(_SUFFIX, '');
}
return text;
}
encodeEncryption(encryptedText, hint) {
if (!encryptedText.contains(_PREFIX_OBSOLETE) && !encryptedText.contains(_PREFIX_A) && !encryptedText.contains(_SUFFIX)) {
if (hint) {
return _PREFIX_A.concat(_HINT, hint, _HINT, encryptedText, _SUFFIX);
}
return _PREFIX_A.concat(encryptedText, _SUFFIX);
}
return encryptedText;
}
}
class SelectionAnalysis {
}
class Encryptable {
}
class Decryptable {
}
module.exports = MeldEncrypt;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsiLi4vbm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsIi4uL3NyYy9EZWNyeXB0TW9kYWwudHMiLCIuLi9zcmMvUGFzc3dvcmRNb2RhbC50cyIsIi4uL3NyYy9DcnlwdG9IZWxwZXIudHMiLCIuLi9zcmMvTWVsZEVuY3J5cHRTZXR0aW5nc1RhYi50cyIsIi4uL3NyYy9tYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qISAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5cclxuXHJcblBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG5wdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQuXHJcblxyXG5USEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiIEFORCBUSEUgQVVUSE9SIERJU0NMQUlNUyBBTEwgV0FSUkFOVElFUyBXSVRIXHJcblJFR0FSRCBUTyBUSElTIFNPRlRXQVJFIElOQ0xVRElORyBBTEwgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWVxyXG5BTkQgRklUTkVTUy4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUiBCRSBMSUFCTEUgRk9SIEFOWSBTUEVDSUFMLCBESVJFQ1QsXHJcbklORElSRUNULCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgT1IgQU5ZIERBTUFHRVMgV0hBVFNPRVZFUiBSRVNVTFRJTkcgRlJPTVxyXG5MT1NTIE9GIFVTRSwgREFUQSBPUiBQUk9GSVRTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgTkVHTElHRU5DRSBPUlxyXG5PVEhFUiBUT1JUSU9VUyBBQ1RJT04sIEFSSVNJTkcgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgVVNFIE9SXHJcblBFUkZPUk1BTkNFIE9GIFRISVMgU09GVFdBUkUuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICovXHJcbi8qIGdsb2JhbCBSZWZsZWN0LCBQcm9taXNlICovXHJcblxyXG52YXIgZXh0ZW5kU3RhdGljcyA9IGZ1bmN0aW9uKGQsIGIpIHtcclxuICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHxcclxuICAgICAgICAoeyBfX3Byb3RvX186IFtdIH0gaW5zdGFuY2VvZiBBcnJheSAmJiBmdW5jdGlvbiAoZCwgYikgeyBkLl9fcHJvdG9fXyA9IGI7IH0pIHx8XHJcbiAgICAgICAgZnVuY3Rpb24gKGQsIGIpIHsgZm9yICh2YXIgcCBpbiBiKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGIsIHApKSBkW3BdID0gYltwXTsgfTtcclxuICAgIHJldHVybiBleHRlbmRTdGF0aWNzKGQsIGIpO1xyXG59O1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZXh0ZW5kcyhkLCBiKSB7XHJcbiAgICBpZiAodHlwZW9mIGIgIT09IFwiZnVuY3Rpb25cIiAmJiBiICE9PSBudWxsKVxyXG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDbGFzcyBleHRlbmRzIHZhbHVlIFwiICsgU3RyaW5nKGIpICsgXCIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbFwiKTtcclxuICAgIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbiAgICBmdW5jdGlvbiBfXygpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGQ7IH1cclxuICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTtcclxufVxyXG5cclxuZXhwb3J0IHZhciBfX2Fzc2lnbiA9IGZ1bmN0aW9uKCkge1xyXG4gICAgX19hc3NpZ24gPSBPYmplY3QuYXNzaWduIHx8IGZ1bmN0aW9uIF9fYXNzaWduKHQpIHtcclxuICAgICAgICBmb3IgKHZhciBzLCBpID0gMSwgbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgPCBuOyBpKyspIHtcclxuICAgICAgICAgICAgcyA9IGFyZ3VtZW50c1tpXTtcclxuICAgICAgICAgICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApKSB0W3BdID0gc1twXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHQ7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gX19hc3NpZ24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcmVzdChzLCBlKSB7XHJcbiAgICB2YXIgdCA9IHt9O1xyXG4gICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApICYmIGUuaW5kZXhPZihwKSA8IDApXHJcbiAgICAgICAgdFtwXSA9IHNbcF07XHJcbiAgICBpZiAocyAhPSBudWxsICYmIHR5cGVvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzID09PSBcImZ1bmN0aW9uXCIpXHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIHAgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKHMpOyBpIDwgcC5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICBpZiAoZS5pbmRleE9mKHBbaV0pIDwgMCAmJiBPYmplY3QucHJvdG90eXBlLnByb3BlcnR5SXNFbnVtZXJhYmxlLmNhbGwocywgcFtpXSkpXHJcbiAgICAgICAgICAgICAgICB0W3BbaV1dID0gc1twW2ldXTtcclxuICAgICAgICB9XHJcbiAgICByZXR1cm4gdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpIHtcclxuICAgIHZhciBjID0gYXJndW1lbnRzLmxlbmd0aCwgciA9IGMgPCAzID8gdGFyZ2V0IDogZGVzYyA9PT0gbnVsbCA/IGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHRhcmdldCwga2V5KSA6IGRlc2MsIGQ7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUgPT09IFwiZnVuY3Rpb25cIikgciA9IFJlZmxlY3QuZGVjb3JhdGUoZGVjb3JhdG9ycyw