You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

744 lines
120 KiB

3 years ago
'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 = '') {
super(app);
this.decryptInPlace = false;
this.text = text;
this.titleEl.innerText = title;
}
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('');
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 {
3 years ago
constructor(app, isEncrypting, confirmPassword, defaultPassword = null, hint) {
3 years ago
super(app);
this.password = null;
3 years ago
this.hint = null;
3 years ago
this.defaultPassword = null;
this.defaultPassword = defaultPassword;
this.confirmPassword = confirmPassword;
3 years ago
this.isEncrypting = isEncrypting;
this.hint = hint;
3 years ago
}
onOpen() {
3 years ago
var _a, _b, _c;
3 years ago
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 */
3 years ago
const confirmPwShown = this.confirmPassword;
3 years ago
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();
});
}
3 years ago
if (!confirmPwShown) {
confirmPwContainerEl.hide();
}
3 years ago
/* End Confirm password input row */
3 years ago
/* 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 */
3 years ago
const confirmPwButtonEl = contentEl.createEl('button', {
text: 'Confirm',
cls: 'meld-e-button-confirm'
});
confirmPwButtonEl.addEventListener('click', (ev) => {
3 years ago
if (validate()) {
this.close();
}
else {
pwInputEl.focus();
}
});
const validate = () => {
if (confirmPwShown) {
if (pwInputEl.value != pwConfirmInputEl.value) {
3 years ago
// passwords don't match
messageEl.setText('Passwords don\'t match');
messageEl.show();
3 years ago
return false;
3 years ago
}
}
3 years ago
this.password = pwInputEl.value;
this.hint = hintInputEl.value;
return true;
};
3 years ago
const inputPasswordHandler = () => {
3 years ago
if (confirmPwShown) {
3 years ago
pwConfirmInputEl.focus();
3 years ago
return;
3 years ago
}
3 years ago
if (hintInputShown) {
hintInputEl.focus();
return;
}
if (validate()) {
3 years ago
this.close();
}
};
const confirmPasswordHandler = () => {
3 years ago
if (validate()) {
if (hintInputShown) {
hintInputEl.focus();
}
else {
this.close();
}
}
};
const hintPasswordHandler = () => {
if (validate()) {
3 years ago
this.close();
}
else {
3 years ago
pwInputEl.focus();
3 years ago
}
};
3 years ago
hintInputEl.addEventListener('keypress', (ev) => {
if ((ev.code === 'Enter' || ev.code === 'NumpadEnter')
&& pwInputEl.value.length > 0) {
ev.preventDefault();
hintPasswordHandler();
}
});
3 years ago
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('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})`;
}
}
3 years ago
const _PREFIX = '%%🔐';
const _PREFIX_OBSOLETE = _PREFIX + ' ';
const _PREFIX_A = _PREFIX + 'α ';
3 years ago
const _SUFFIX = ' 🔐%%';
3 years ago
const _HINT = '💡';
3 years ago
const DEFAULT_SETTINGS = {
expandToWholeLines: true,
confirmPassword: true,
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
}
3 years ago
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);
}
}
3 years ago
const selectionText = editor.getRange(startPos, endPos);
return this.processSelection(checking, editor, selectionText, startPos, endPos, decryptInPlace);
}
3 years ago
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"));
let maxOffset = editor.posToOffset({ line: editor.lastLine(), ch: Number.MAX_VALUE });
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;
}
3 years ago
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;
3 years ago
if (result.canDecrypt) {
result.decryptable = this.parseDecryptableContent(selectionText);
if (result.decryptable == null) {
result.canDecrypt = false;
}
}
3 years ago
return result;
}
processSelection(checking, editor, selectionText, finalSelectionStart, finalSelectionEnd, decryptInPlace) {
3 years ago
var _a;
3 years ago
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 = '';
}
3 years ago
const pwModal = new PasswordModal(this.app, selectionAnalysis.canEncrypt, confirmPassword, this.passwordLastUsed, (_a = selectionAnalysis.decryptable) === null || _a === void 0 ? void 0 : _a.hint);
3 years ago
pwModal.onClose = () => {
var _a;
const pw = (_a = pwModal.password) !== null && _a !== void 0 ? _a : '';
if (pw.length == 0) {
return;
}
3 years ago
const hint = pwModal.hint;
3 years ago
// 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) {
3 years ago
const encryptable = new Encryptable();
encryptable.text = selectionText;
encryptable.hint = hint;
this.encryptSelection(editor, encryptable, pw, finalSelectionStart, finalSelectionEnd);
3 years ago
}
else {
3 years ago
if (selectionAnalysis.decryptable.version == 1) {
this.decryptSelection_a(editor, selectionAnalysis.decryptable, pw, finalSelectionStart, finalSelectionEnd, decryptInPlace);
3 years ago
}
else {
3 years ago
this.decryptSelectionObsolete(editor, selectionAnalysis.decryptable, pw, finalSelectionStart, finalSelectionEnd, decryptInPlace);
3 years ago
}
}
};
pwModal.open();
return true;
}
3 years ago
encryptSelection(editor, encryptable, password, finalSelectionStart, finalSelectionEnd) {
3 years ago
return __awaiter(this, void 0, void 0, function* () {
//encrypt
const crypto = new CryptoHelperV2();
3 years ago
const encodedText = this.encodeEncryption(yield crypto.encryptToBase64(encryptable.text, password), encryptable.hint);
3 years ago
editor.setSelection(finalSelectionStart, finalSelectionEnd);
3 years ago
editor.replaceSelection(encodedText);
3 years ago
});
}
3 years ago
decryptSelection_a(editor, decryptable, password, selectionStart, selectionEnd, decryptInPlace) {
3 years ago
return __awaiter(this, void 0, void 0, function* () {
// decrypt
const crypto = new CryptoHelperV2();
3 years ago
const decryptedText = yield crypto.decryptFromBase64(decryptable.base64CipherText, password);
3 years ago
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);
decryptModal.onClose = () => {
editor.focus();
if (decryptModal.decryptInPlace) {
editor.setSelection(selectionStart, selectionEnd);
editor.replaceSelection(decryptedText);
}
};
decryptModal.open();
}
}
});
}
3 years ago
decryptSelectionObsolete(editor, decryptable, password, selectionStart, selectionEnd, decryptInPlace) {
3 years ago
return __awaiter(this, void 0, void 0, function* () {
// decrypt
3 years ago
const base64CipherText = this.removeMarkers(decryptable.base64CipherText);
3 years ago
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);
decryptModal.onClose = () => {
editor.focus();
if (decryptModal.decryptInPlace) {
editor.setSelection(selectionStart, selectionEnd);
editor.replaceSelection(decryptedText);
}
};
decryptModal.open();
}
}
});
}
3 years ago
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;
}
3 years ago
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;
}
3 years ago
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);
3 years ago
}
3 years ago
return encryptedText;
3 years ago
}
}
class SelectionAnalysis {
3 years ago
}
class Encryptable {
}
class Decryptable {
3 years ago
}
module.exports = MeldEncrypt;
3 years ago
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsiLi4vbm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsIi4uL3NyYy9EZWNyeXB0TW9kYWwudHMiLCIuLi9zcmMvUGFzc3dvcmRNb2RhbC50cyIsIi4uL3NyYy9DcnlwdG9IZWxwZXIudHMiLCIuLi9zcmMvTWVsZEVuY3J5cHRTZXR0aW5nc1RhYi50cyIsIi4uL3NyYy9tYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qISAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5cclxuXHJcblBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG5wdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQuXHJcblxyXG5USEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiIEFORCBUSEUgQVVUSE9SIERJU0NMQUlNUyBBTEwgV0FSUkFOVElFUyBXSVRIXHJcblJFR0FSRCBUTyBUSElTIFNPRlRXQVJFIElOQ0xVRElORyBBTEwgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWVxyXG5BTkQgRklUTkVTUy4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUiBCRSBMSUFCTEUgRk9SIEFOWSBTUEVDSUFMLCBESVJFQ1QsXHJcbklORElSRUNULCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgT1IgQU5ZIERBTUFHRVMgV0hBVFNPRVZFUiBSRVNVTFRJTkcgRlJPTVxyXG5MT1NTIE9GIFVTRSwgREFUQSBPUiBQUk9GSVRTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgTkVHTElHRU5DRSBPUlxyXG5PVEhFUiBUT1JUSU9VUyBBQ1RJT04sIEFSSVNJTkcgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgVVNFIE9SXHJcblBFUkZPUk1BTkNFIE9GIFRISVMgU09GVFdBUkUuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICovXHJcbi8qIGdsb2JhbCBSZWZsZWN0LCBQcm9taXNlICovXHJcblxyXG52YXIgZXh0ZW5kU3RhdGljcyA9IGZ1bmN0aW9uKGQsIGIpIHtcclxuICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHxcclxuICAgICAgICAoeyBfX3Byb3RvX186IFtdIH0gaW5zdGFuY2VvZiBBcnJheSAmJiBmdW5jdGlvbiAoZCwgYikgeyBkLl9fcHJvdG9fXyA9IGI7IH0pIHx8XHJcbiAgICAgICAgZnVuY3Rpb24gKGQsIGIpIHsgZm9yICh2YXIgcCBpbiBiKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGIsIHApKSBkW3BdID0gYltwXTsgfTtcclxuICAgIHJldHVybiBleHRlbmRTdGF0aWNzKGQsIGIpO1xyXG59O1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZXh0ZW5kcyhkLCBiKSB7XHJcbiAgICBpZiAodHlwZW9mIGIgIT09IFwiZnVuY3Rpb25cIiAmJiBiICE9PSBudWxsKVxyXG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDbGFzcyBleHRlbmRzIHZhbHVlIFwiICsgU3RyaW5nKGIpICsgXCIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbFwiKTtcclxuICAgIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbiAgICBmdW5jdGlvbiBfXygpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGQ7IH1cclxuICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTtcclxufVxyXG5cclxuZXhwb3J0IHZhciBfX2Fzc2lnbiA9IGZ1bmN0aW9uKCkge1xyXG4gICAgX19hc3NpZ24gPSBPYmplY3QuYXNzaWduIHx8IGZ1bmN0aW9uIF9fYXNzaWduKHQpIHtcclxuICAgICAgICBmb3IgKHZhciBzLCBpID0gMSwgbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgPCBuOyBpKyspIHtcclxuICAgICAgICAgICAgcyA9IGFyZ3VtZW50c1tpXTtcclxuICAgICAgICAgICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApKSB0W3BdID0gc1twXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHQ7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gX19hc3NpZ24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcmVzdChzLCBlKSB7XHJcbiAgICB2YXIgdCA9IHt9O1xyXG4gICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApICYmIGUuaW5kZXhPZihwKSA8IDApXHJcbiAgICAgICAgdFtwXSA9IHNbcF07XHJcbiAgICBpZiAocyAhPSBudWxsICYmIHR5cGVvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzID09PSBcImZ1bmN0aW9uXCIpXHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIHAgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKHMpOyBpIDwgcC5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICBpZiAoZS5pbmRleE9mKHBbaV0pIDwgMCAmJiBPYmplY3QucHJvdG90eXBlLnByb3BlcnR5SXNFbnVtZXJhYmxlLmNhbGwocywgcFtpXSkpXHJcbiAgICAgICAgICAgICAgICB0W3BbaV1dID0gc1twW2ldXTtcclxuICAgICAgICB9XHJcbiAgICByZXR1cm4gdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpIHtcclxuICAgIHZhciBjID0gYXJndW1lbnRzLmxlbmd0aCwgciA9IGMgPCAzID8gdGFyZ2V0IDogZGVzYyA9PT0gbnVsbCA/IGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHRhcmdldCwga2V5KSA6IGRlc2MsIGQ7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUgPT09IFwiZnVuY3Rpb25cIikgciA9IFJlZmxlY3QuZGVjb3JhdGUoZGVjb3JhdG9ycyw