/* encryptcontent/decrypt-contents.tpl.js */ function base64url_decode(input) { try { const binString = atob(input.replace(/-/g, '+').replace(/_/g, '/')); const binArray = Uint8Array.from(binString, (m) => m.codePointAt(0)); return new TextDecoder().decode(binArray); } catch (err) { return false; } } /* Decrypts the key from the key bundle. */ function decrypt_key(pass, iv_b64, ciphertext_b64, salt_b64) { let salt = CryptoJS.enc.Base64.parse(salt_b64), kdfcfg = {keySize: 256 / 32,hasher: CryptoJS.algo.SHA256,iterations: encryptcontent_obfuscate ? 1 : 10000}; let kdfkey = CryptoJS.PBKDF2(pass, salt,kdfcfg); let iv = CryptoJS.enc.Base64.parse(iv_b64), ciphertext = CryptoJS.enc.Base64.parse(ciphertext_b64); let encrypted = {ciphertext: ciphertext}, cfg = {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}; let key = CryptoJS.AES.decrypt(encrypted, kdfkey, cfg); try { let keystore = JSON.parse(key.toString(CryptoJS.enc.Utf8)); if (encryptcontent_id in keystore) { return keystore; } else { //id not found in keystore return false; } } catch (err) { // encoding failed; wrong password return false; } }; /* Split key bundle and try to decrypt it */ function decrypt_key_from_bundle(password, ciphertext_bundle, username) { // grab the ciphertext bundle and try to decrypt it let user, pass; let parts, keys, userhash; if (ciphertext_bundle) { if (username) { user = encodeURIComponent(username.toLowerCase()); userhash = CryptoJS.SHA256(user).toString(CryptoJS.enc.Base64); } for (let i = 0; i < ciphertext_bundle.length; i++) { pass = encodeURIComponent(password); parts = ciphertext_bundle[i].split(';'); if (parts.length == 3) { keys = decrypt_key(pass, parts[0], parts[1], parts[2]); if (keys) { return keys; } } else if (parts.length == 4 && username) { if (parts[3] == userhash) { keys = decrypt_key(pass, parts[0], parts[1], parts[2]); if (keys) { return keys; } } } } } return false; }; /* Decrypts the content from the ciphertext bundle. */ function decrypt_content(key, iv_b64, ciphertext_b64) { let iv = CryptoJS.enc.Base64.parse(iv_b64), ciphertext = CryptoJS.enc.Base64.parse(ciphertext_b64); let encrypted = {ciphertext: ciphertext}, cfg = {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}; let plaintext = CryptoJS.AES.decrypt(encrypted, CryptoJS.enc.Hex.parse(key), cfg); if(plaintext.sigBytes >= 0) { try { return plaintext.toString(CryptoJS.enc.Utf8) } catch (err) { // encoding failed; wrong key return false; } } else { // negative sigBytes; wrong key return false; } }; /* Split cyphertext bundle and try to decrypt it */ function decrypt_content_from_bundle(key, ciphertext_bundle) { // grab the ciphertext bundle and try to decrypt it if (ciphertext_bundle) { let parts = ciphertext_bundle.split(';'); if (parts.length == 2) { return decrypt_content(key, parts[0], parts[1]); } } return false; }; /* Save decrypted keystore to sessionStorage */ function setKeys(keys_from_keystore) { for (const id in keys_from_keystore) { sessionStorage.setItem(id, keys_from_keystore[id]); } }; /* Delete key with specific name in sessionStorage */ function delItemName(key) { sessionStorage.removeItem(key); }; function getItemName(key) { return sessionStorage.getItem(key); }; /* Reload scripts src after decryption process */ function reload_js(src) { let script_src, script_tag, new_script_tag; let head = document.getElementsByTagName('head')[0]; if (src.startsWith('#')) { script_tag = document.getElementById(src.substr(1)); if (script_tag) { script_tag.remove(); new_script_tag = document.createElement('script'); if (script_tag.innerHTML) { new_script_tag.innerHTML = script_tag.innerHTML; } if (script_tag.src) { new_script_tag.src = script_tag.src; } if (script_tag.type) { new_script_tag.type = script_tag.type; } head.appendChild(new_script_tag); } } else { if (base_url == '.') { script_src = src; } else { script_src = base_url + '/' + src; } script_tag = document.querySelector('script[src="' + script_src + '"]'); if (script_tag) { script_tag.remove(); new_script_tag = document.createElement('script'); new_script_tag.src = script_src; head.appendChild(new_script_tag); } } }; /* Decrypt speficique html entry from mkdocs configuration */ function decrypt_somethings(key, encrypted_something) { var html_item = ''; for (const [name, tag] of Object.entries(encrypted_something)) { if (tag[1] == 'id') { html_item = [document.getElementById(name)]; } else if (tag[1] == 'class') { html_item = document.getElementsByClassName(name); } else { console.log('WARNING: Unknow tag html found, check "encrypted_something" configuration.'); } if (html_item[0]) { for (let i = 0; i < html_item.length; i++) { // grab the cipher bundle if something exist if (html_item[i].style.display == "none") { let content = decrypt_content_from_bundle(key, html_item[i].innerHTML); if (content !== false) { // success; display the decrypted content html_item[i].innerHTML = content; html_item[i].style.display = null; // any post processing on the decrypted content should be done here } } } } } return html_item[0]; }; /* Decrypt content of a page */ function decrypt_action(username_input, password_input, encrypted_content, decrypted_content, key_from_storage=false) { let key=false; let keys_from_keystore=false; let user=false; if (username_input) { user = username_input.value; } if (key_from_storage !== false) { key = key_from_storage; } else { keys_from_keystore = decrypt_key_from_bundle(password_input.value, encryptcontent_keystore, user); if (keys_from_keystore) { key = keys_from_keystore[encryptcontent_id]; } } let content = false; if (key) { content = decrypt_content_from_bundle(key, encrypted_content.innerHTML); } if (content !== false) { // success; display the decrypted content decrypted_content.innerHTML = content; encrypted_content.parentNode.removeChild(encrypted_content); if (keys_from_keystore !== false) { return keys_from_keystore } else { return key } } else { return false } }; function decryptor_reaction(key_or_keys, password_input, decrypted_content, fallback_used=false) { if (key_or_keys) { let key; if (typeof key_or_keys === "object") { key = key_or_keys[encryptcontent_id]; setKeys(key_or_keys); } else { key = key_or_keys; } // continue to decrypt others parts if (typeof inject_something !== 'undefined') { decrypted_content = decrypt_somethings(key, inject_something); } if (typeof delete_something !== 'undefined') { let el = document.getElementById(delete_something) if (el) { el.remove(); } } // any post processing on the decrypted content should be done here document$.next(document); if (typeof theme_run_after_decryption !== 'undefined') { theme_run_after_decryption(); } if (window.location.hash) { //jump to anchor if hash given after decryption window.location.href = window.location.hash; } } else { // remove item on sessionStorage if decryption process fail (Invalid item) if (!fallback_used) { if (!encryptcontent_obfuscate) { // create HTML element for the inform message let mkdocs_decrypt_msg = document.getElementById('mkdocs-decrypt-msg'); mkdocs_decrypt_msg.textContent = decryption_failure_message; password_input.value = ''; password_input.focus(); } } delItemName(encryptcontent_id); } } /* Trigger decryption process */ function init_decryptor() { let username_input = document.getElementById('mkdocs-content-user'); let password_input = document.getElementById('mkdocs-content-password'); // adjust password field width to placeholder length //if (password_input.hasAttribute('placeholder')) { // password_input.setAttribute('size', password_input.getAttribute('placeholder').length); //} let encrypted_content = document.getElementById('mkdocs-encrypted-content'); let decrypted_content = document.getElementById('mkdocs-decrypted-content'); let content_decrypted; /* If remember_keys is set, try to use sessionStorage item to decrypt content when page is loaded */ let key_from_storage = getItemName(encryptcontent_id); if (key_from_storage) { content_decrypted = decrypt_action( username_input, password_input, encrypted_content, decrypted_content, key_from_storage ); decryptor_reaction(content_decrypted, password_input, decrypted_content, true); } /* If password_button is set, try decrypt content when button is press */ let decrypt_button = document.getElementById("mkdocs-decrypt-button"); if (decrypt_button) { decrypt_button.onclick = function(event) { event.preventDefault(); content_decrypted = decrypt_action( username_input, password_input, encrypted_content, decrypted_content ); decryptor_reaction(content_decrypted, password_input, decrypted_content); }; } /* Default, try decrypt content when key enter is press */ password_input.addEventListener('keypress', function(event) { if (event.key === "Enter") { event.preventDefault(); content_decrypted = decrypt_action( username_input, password_input, encrypted_content, decrypted_content ); decryptor_reaction(content_decrypted, password_input, decrypted_content); } }); } if (typeof base_url === 'undefined') { var base_url = JSON.parse(document.getElementById('__config').textContent).base; } if (document.readyState === "loading") { // Loading hasn't finished yet document.addEventListener("DOMContentLoaded", init_decryptor); } else { // `DOMContentLoaded` has already fired init_decryptor(); } window["init_decryptor"] = init_decryptor;