|
@@ -0,0 +1,164 @@
|
|
|
|
+{{/*
|
|
|
|
+ ## Hugo Encrypt
|
|
|
|
+ ### Params:
|
|
|
|
+ - `password`:
|
|
|
|
+
|
|
|
|
+ require param
|
|
|
|
+ - Simple
|
|
|
|
+
|
|
|
|
+ {{< hugo-encrypt "your password" >}}
|
|
|
|
+ your content
|
|
|
|
+ {{< /hugo-encrypt >}}
|
|
|
|
+
|
|
|
|
+*/}}
|
|
|
|
+{{/* DEFAULTS */}}
|
|
|
|
+{{ $_hugo_config := `{ "version": 1 }` }}
|
|
|
|
+
|
|
|
|
+<hugo-encrypt>
|
|
|
|
+ {{ if .Get 0 }}
|
|
|
|
+ {{- $password := $.Scratch.Set "password" (.Get 0) -}}
|
|
|
|
+ {{ else if .Site.Params.Passwoird }}
|
|
|
|
+ {{- $password := $.Scratch.Set "password" .Site.Params.Password -}}
|
|
|
|
+ {{ else }}
|
|
|
|
+ {{- $password -}}
|
|
|
|
+ {{ end }}
|
|
|
|
+ <p>{{ i18n "protectedbypwd" }}</p>
|
|
|
|
+
|
|
|
|
+ <div class='hugo-encrypt-form'>
|
|
|
|
+ <input
|
|
|
|
+ class="hugo-encrypt-input"
|
|
|
|
+ id="hugo-encrypt-password"
|
|
|
|
+ placeholder='{{ i18n "inputpassword" }}'
|
|
|
|
+ />
|
|
|
|
+ <input
|
|
|
|
+ class="hugo-encrypt-button"
|
|
|
|
+ type="button"
|
|
|
|
+ value='{{ i18n "decrypt" }}'
|
|
|
|
+ id="button" onclick="hugoDecrypt(document.getElementById('hugo-encrypt-password').value,'input')"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ <cipher-text data-password="{{ $password }}" style="display:none;">
|
|
|
|
+<!-- Do not indent the following two lines -->
|
|
|
|
+<p id="verifyText" style="display:none;">The quick brown fox jumps over the lazy dog</p>
|
|
|
|
+{{ .Inner }}
|
|
|
|
+ </cipher-text>
|
|
|
|
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.0/showdown.min.js"></script>
|
|
|
|
+ <script>
|
|
|
|
+ let cipher = document.getElementsByTagName("cipher-text")[0];
|
|
|
|
+ const storageKey = location.pathname + "password";
|
|
|
|
+ const userStorage = {{ if .Site.Params.hugoEncryptStorage }} window['{{.Site.Params.hugoEncryptStorage}}Storage'] {{ else }} localStorage {{ end }};
|
|
|
|
+ /**
|
|
|
|
+ * Encodes a utf8 string as a byte array.
|
|
|
|
+ * @param {String} str
|
|
|
|
+ * @returns {Uint8Array}
|
|
|
|
+ */
|
|
|
|
+ function str2buf(str) {
|
|
|
|
+ return new TextEncoder("utf-8").encode(str);
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * Decodes a byte array as a utf8 string.
|
|
|
|
+ * @param {Uint8Array} buffer
|
|
|
|
+ * @returns {String}
|
|
|
|
+ */
|
|
|
|
+ function buf2str(buffer) {
|
|
|
|
+ return new TextDecoder("utf-8").decode(buffer);
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * Decodes a string of hex to a byte array.
|
|
|
|
+ * @param {String} hexStr
|
|
|
|
+ * @returns {Uint8Array}
|
|
|
|
+ */
|
|
|
|
+ function hex2buf(hexStr) {
|
|
|
|
+ return new Uint8Array(hexStr.match(/.{2}/g).map(h => parseInt(h, 16)));
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * Given a passphrase, this generates a crypto key
|
|
|
|
+ * using `PBKDF2` with SHA256 and 1000 iterations.
|
|
|
|
+ * If no salt is given, a new one is generated.
|
|
|
|
+ * The return value is an array of `[key, salt]`.
|
|
|
|
+ * @param {String} passphrase
|
|
|
|
+ * @param {UInt8Array} salt [salt=random bytes]
|
|
|
|
+ * @returns {Promise<[CryptoKey,UInt8Array]>}
|
|
|
|
+ */
|
|
|
|
+ function deriveKey(passphrase, salt) {
|
|
|
|
+ salt = salt || crypto.getRandomValues(new Uint8Array(8));
|
|
|
|
+ return crypto.subtle
|
|
|
|
+ .importKey("raw", str2buf(passphrase), "PBKDF2", false, ["deriveKey"])
|
|
|
|
+ .then(key =>
|
|
|
|
+ crypto.subtle.deriveKey(
|
|
|
|
+ { name: "PBKDF2", salt, iterations: 1000, hash: "SHA-256" },
|
|
|
|
+ key,
|
|
|
|
+ { name: "AES-GCM", length: 256 },
|
|
|
|
+ false,
|
|
|
|
+ ["encrypt", "decrypt"],
|
|
|
|
+ ),
|
|
|
|
+ )
|
|
|
|
+ .then(key => [key, salt]);
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * Given a key and ciphertext (in the form of a string) as given by `encrypt`,
|
|
|
|
+ * this decrypts the ciphertext and returns the original plaintext
|
|
|
|
+ * @param {String} passphrase
|
|
|
|
+ * @param {String} saltIvCipherHex
|
|
|
|
+ * @returns {Promise<String>}
|
|
|
|
+ */
|
|
|
|
+ function decrypt(passphrase, saltIvCipherHex) {
|
|
|
|
+ const [salt, iv, data] = saltIvCipherHex.split("-").map(hex2buf);
|
|
|
|
+ return deriveKey(passphrase, salt)
|
|
|
|
+ .then(([key]) => crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data))
|
|
|
|
+ .then(v => buf2str(new Uint8Array(v)));
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * The html entities in the decrypted text need to be decoded
|
|
|
|
+ * before converting any markdown to html
|
|
|
|
+ */
|
|
|
|
+ function htmlDecode(input){
|
|
|
|
+ var doc = new DOMParser().parseFromString(input, "text/html");
|
|
|
|
+ return doc.documentElement.textContent;
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * Needed to convert markdown within the decrypted text to html
|
|
|
|
+ */
|
|
|
|
+ function interpreteMarkdown(input) {
|
|
|
|
+ var converter = new showdown.Converter()
|
|
|
|
+ return converter.makeHtml(input)
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * @name:hugoDecrypt
|
|
|
|
+ * @description: judge the password ,and decrypt post
|
|
|
|
+ * @param {String} password
|
|
|
|
+ * @param {String} type
|
|
|
|
+ */
|
|
|
|
+ const hugoDecrypt = function(password, type) {
|
|
|
|
+ try {
|
|
|
|
+ decrypt(password, cipher.innerText).then(function(res) {
|
|
|
|
+ if ( res.includes("The quick brown fox jumps over the lazy dog") ) {
|
|
|
|
+ cipher.parentElement.outerHTML = interpreteMarkdown(htmlDecode(res));
|
|
|
|
+ userStorage.setItem(storageKey, password);
|
|
|
|
+ document.getElementById("verifyText").outerHTML = "";
|
|
|
|
+ } else {
|
|
|
|
+ if (type === "input") {
|
|
|
|
+ alert('{{ i18n "wrongpwd" }}');
|
|
|
|
+ } else if (type === "storage") {
|
|
|
|
+ userStorage.removeItem(storageKey);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ } catch (error) {
|
|
|
|
+ if (type === "input") {
|
|
|
|
+ alert('{{ i18n "wrongpwd" }}');
|
|
|
|
+ } else if (type === "storage") {
|
|
|
|
+ userStorage.removeItem(location.pathname + "password");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ </script>
|
|
|
|
+ <script>
|
|
|
|
+ window.onload = () => {
|
|
|
|
+ if (userStorage.getItem(storageKey)) {
|
|
|
|
+ hugoDecrypt(userStorage.getItem(storageKey), "storage");
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ </script>
|
|
|
|
+</hugo-encrypt>
|
|
|
|
+
|