|
@@ -0,0 +1,140 @@
|
|
|
+{{ $_hugo_config := `{ "version": 1 }` }}
|
|
|
+
|
|
|
+<hugo-encrypt>
|
|
|
+ {{ if .Get 0 }}
|
|
|
+ {{- $passphrase := $.Scratch.Set "passphrase" (.Get 0) -}}
|
|
|
+ {{ else if .Site.Params.HugoEncrypt.Password }}
|
|
|
+ {{- $passphrase := $.Scratch.Set "passphrase" .Site.Params.HugoEncrypt.Password -}}
|
|
|
+ {{ else }}
|
|
|
+ {{- $passphrase -}}
|
|
|
+ {{ end }}
|
|
|
+ <p>{{ i18n "protectedByPassword" }}</p>
|
|
|
+
|
|
|
+ <div class='hugo-encrypt-form'>
|
|
|
+ <input
|
|
|
+ class="hugo-encrypt-input"
|
|
|
+ id="hugo-encrypt-password"
|
|
|
+ placeholder='{{ i18n "enterPassword" }}'
|
|
|
+ />
|
|
|
+ <input
|
|
|
+ class="hugo-encrypt-button"
|
|
|
+ type="button"
|
|
|
+ value='{{ i18n "decrypt" }}'
|
|
|
+ id="button" onclick="hugoDecrypt(document.getElementById('hugo-encrypt-password').value,'input')"
|
|
|
+ />
|
|
|
+ <div id="input-response"></div>
|
|
|
+ </div>
|
|
|
+ <cipher-text data-password='{{ $.Scratch.Get "passphrase" }}' style="display:none;">
|
|
|
+ {{ .Inner }}
|
|
|
+ </cipher-text>
|
|
|
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/core.js"></script>
|
|
|
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/sha1.js"></script>
|
|
|
+ <script>
|
|
|
+ let cipher = document.getElementsByTagName("cipher-text")[0];
|
|
|
+ const storageKey = location.pathname + "password";
|
|
|
+ const userStorage = {{ if .Site.Params.HugoEncrypt.Storage }} window['{{.Site.Params.HugoEncrypt.Storage}}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)));
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ /**
|
|
|
+ * @name:hugoDecrypt
|
|
|
+ * @description: judge the password ,and decrypt post
|
|
|
+ * @param {String} password
|
|
|
+ * @param {String} type
|
|
|
+ */
|
|
|
+ const hugoDecrypt = function(password, type) {
|
|
|
+ decrypt(password, cipher.innerText).then(function(decrypted_text) {
|
|
|
+ /**
|
|
|
+ * calculate sha1 of decrypted text and check if it
|
|
|
+ * matches the sha1 at the bottom of the decrypted text
|
|
|
+ * to get the same hash that was added during encryption we
|
|
|
+ * need to remove the last line
|
|
|
+ */
|
|
|
+ let hash = CryptoJS.SHA1(decrypted_text.replace(/\r?\n?[^\r\n]*$/, ""));
|
|
|
+ let sha1_sum = CryptoJS.enc.Hex.stringify(hash);
|
|
|
+
|
|
|
+ if ( decrypted_text.includes(sha1_sum) ) {
|
|
|
+ cipher.parentElement.outerHTML = decrypted_text;
|
|
|
+ userStorage.setItem(storageKey, password);
|
|
|
+ document.getElementById("sha1sum").innerHTML = "Success: " + sha1_sum;
|
|
|
+ console.log("Decryption successful. Storing password in {{.Site.Params.HugoEncrypt.Storage}}Storage.");
|
|
|
+ }
|
|
|
+ }).catch(function(error) {
|
|
|
+ if (type === "input") {
|
|
|
+ document.getElementById("input-response").innerHTML = "{{ i18n "wrongPassword" }}";
|
|
|
+ console.log('{{ i18n "wrongPassword" }}', error);
|
|
|
+ } else if (type === "storage") {
|
|
|
+ userStorage.removeItem(location.pathname + "password");
|
|
|
+ console.log("Password changed. Clearing userStorage.", error);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+ </script>
|
|
|
+ <script>
|
|
|
+ window.onload = () => {
|
|
|
+ if (userStorage.getItem(storageKey)) {
|
|
|
+ console.log("Found storageKey in userStorage. Attemtping decryption");
|
|
|
+ hugoDecrypt(userStorage.getItem(storageKey), "storage");
|
|
|
+ }
|
|
|
+ };
|
|
|
+ </script>
|
|
|
+</hugo-encrypt>
|