hugo-encrypt.html 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. {{ $_hugo_config := `{ "version": 1 }` }}
  2. <hugo-encrypt>
  3. {{ if .Get 0 }}
  4. {{- $passphrase := $.Scratch.Set "passphrase" (.Get 0) -}}
  5. {{ else if .Site.Params.HugoEncrypt.Password }}
  6. {{- $passphrase := $.Scratch.Set "passphrase" .Site.Params.HugoEncrypt.Password -}}
  7. {{ else }}
  8. {{- $passphrase -}}
  9. {{ end }}
  10. <p>{{ i18n "protectedByPassword" }}</p>
  11. <div class='hugo-encrypt-form'>
  12. <input
  13. class="hugo-encrypt-input"
  14. id="hugo-encrypt-password"
  15. placeholder='{{ i18n "enterPassword" }}'
  16. />
  17. <input
  18. class="hugo-encrypt-button"
  19. type="button"
  20. value='{{ i18n "decrypt" }}'
  21. id="button" onclick="hugoDecrypt(document.getElementById('hugo-encrypt-password').value,'input')"
  22. />
  23. <div id="input-response"></div>
  24. </div>
  25. <cipher-text data-password='{{ $.Scratch.Get "passphrase" }}' style="display:none;">
  26. {{ .Inner }}
  27. </cipher-text>
  28. <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/core.js"></script>
  29. <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/sha1.js"></script>
  30. <script>
  31. let cipher = document.getElementsByTagName("cipher-text")[0];
  32. const storageKey = location.pathname + "password";
  33. const userStorage = {{ if .Site.Params.HugoEncrypt.Storage }} window['{{.Site.Params.HugoEncrypt.Storage}}Storage'] {{ else }} localStorage {{ end }};
  34. /**
  35. * Encodes a utf8 string as a byte array.
  36. * @param {String} str
  37. * @returns {Uint8Array}
  38. */
  39. function str2buf(str) {
  40. return new TextEncoder("utf-8").encode(str);
  41. }
  42. /**
  43. * Decodes a byte array as a utf8 string.
  44. * @param {Uint8Array} buffer
  45. * @returns {String}
  46. */
  47. function buf2str(buffer) {
  48. return new TextDecoder("utf-8").decode(buffer);
  49. }
  50. /**
  51. * Decodes a string of hex to a byte array.
  52. * @param {String} hexStr
  53. * @returns {Uint8Array}
  54. */
  55. function hex2buf(hexStr) {
  56. return new Uint8Array(hexStr.match(/.{2}/g).map(h => parseInt(h, 16)));
  57. }
  58. /**
  59. * Given a passphrase, this generates a crypto key
  60. * using `PBKDF2` with SHA256 and 1000 iterations.
  61. * If no salt is given, a new one is generated.
  62. * The return value is an array of `[key, salt]`.
  63. * @param {String} passphrase
  64. * @param {UInt8Array} salt [salt=random bytes]
  65. * @returns {Promise<[CryptoKey,UInt8Array]>}
  66. */
  67. function deriveKey(passphrase, salt) {
  68. salt = salt || crypto.getRandomValues(new Uint8Array(8));
  69. return crypto.subtle
  70. .importKey("raw", str2buf(passphrase), "PBKDF2", false, ["deriveKey"])
  71. .then(key =>
  72. crypto.subtle.deriveKey(
  73. { name: "PBKDF2", salt, iterations: 1000, hash: "SHA-256" },
  74. key,
  75. { name: "AES-GCM", length: 256 },
  76. false,
  77. ["encrypt", "decrypt"],
  78. ),
  79. )
  80. .then(key => [key, salt]);
  81. }
  82. /**
  83. * Given a key and ciphertext (in the form of a string) as given by `encrypt`,
  84. * this decrypts the ciphertext and returns the original plaintext
  85. * @param {String} passphrase
  86. * @param {String} saltIvCipherHex
  87. * @returns {Promise<String>}
  88. */
  89. function decrypt(passphrase, saltIvCipherHex) {
  90. const [salt, iv, data] = saltIvCipherHex.split("-").map(hex2buf);
  91. return deriveKey(passphrase, salt)
  92. .then(([key]) => crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data))
  93. .then(v => buf2str(new Uint8Array(v)));
  94. }
  95. /**
  96. /**
  97. * @name:hugoDecrypt
  98. * @description: judge the password ,and decrypt post
  99. * @param {String} password
  100. * @param {String} type
  101. */
  102. const hugoDecrypt = function(password, type) {
  103. decrypt(password, cipher.innerText).then(function(decrypted_text) {
  104. /**
  105. * calculate sha1 of decrypted text and check if it
  106. * matches the sha1 at the bottom of the decrypted text
  107. * to get the same hash that was added during encryption we
  108. * need to remove the last line
  109. */
  110. let hash = CryptoJS.SHA1(decrypted_text.replace(/\r?\n?[^\r\n]*$/, ""));
  111. let sha1_sum = CryptoJS.enc.Hex.stringify(hash);
  112. if ( decrypted_text.includes(sha1_sum) ) {
  113. cipher.parentElement.outerHTML = decrypted_text;
  114. userStorage.setItem(storageKey, password);
  115. document.getElementById("sha1sum").innerHTML = "Success: " + sha1_sum;
  116. console.log("Decryption successful. Storing password in {{.Site.Params.HugoEncrypt.Storage}}Storage.");
  117. }
  118. }).catch(function(error) {
  119. if (type === "input") {
  120. document.getElementById("input-response").innerHTML = "{{ i18n "wrongPassword" }}";
  121. console.log('{{ i18n "wrongPassword" }}', error);
  122. } else if (type === "storage") {
  123. userStorage.removeItem(location.pathname + "password");
  124. console.log("Password changed. Clearing userStorage.", error);
  125. }
  126. });
  127. };
  128. </script>
  129. <script>
  130. window.onload = () => {
  131. if (userStorage.getItem(storageKey)) {
  132. console.log("Found storageKey in userStorage. Attemtping decryption");
  133. hugoDecrypt(userStorage.getItem(storageKey), "storage");
  134. }
  135. };
  136. </script>
  137. </hugo-encrypt>