pull/177/head
robinmoisson 2023-04-19 11:19:37 +02:00
rodzic f5721f079a
commit b257919458
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 9419716500078583
1 zmienionych plików z 582 dodań i 572 usunięć

Wyświetl plik

@ -1,15 +1,17 @@
<!doctype html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<title>StatiCrypt: Password protect static HTML</title> <title>StatiCrypt: Password protect static HTML</title>
<meta name="description" content=""> <meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" <link
rel="stylesheet"
type="text/css" type="text/css"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous"> crossorigin="anonymous"
/>
<style> <style>
a.no-style { a.no-style {
color: inherit; color: inherit;
@ -33,26 +35,38 @@
<h1> <h1>
StatiCrypt StatiCrypt
<div class="pull-right"> <div class="pull-right">
<iframe src="https://ghbtns.com/github-btn.html?user=robinmoisson&repo=staticrypt&type=star&size=large" <iframe
frameborder="0" scrolling="0" width="80px" height="30px"></iframe> src="https://ghbtns.com/github-btn.html?user=robinmoisson&repo=staticrypt&type=star&size=large"
<iframe src="https://ghbtns.com/github-btn.html?user=robinmoisson&repo=staticrypt&type=fork&size=large" frameborder="0"
frameborder="0" scrolling="0" width="80px" height="30px"></iframe> scrolling="0"
width="80px"
height="30px"
></iframe>
<iframe
src="https://ghbtns.com/github-btn.html?user=robinmoisson&repo=staticrypt&type=fork&size=large"
frameborder="0"
scrolling="0"
width="80px"
height="30px"
></iframe>
</div> </div>
<br> <br />
<small>Password protect a static HTML page</small> <small>Password protect a static HTML page</small>
</h1> </h1>
<p> <p>
StatiCrypt uses AES-256 with WebCrypto to encrypt your html string with your long password, in your browser (client side). StatiCrypt uses AES-256 with WebCrypto to encrypt your html string with your long password, in
your browser (client side).
</p> </p>
<p> <p>
Download your encrypted string in a HTML page with a password prompt you can upload anywhere (see <a Download your encrypted string in a HTML page with a password prompt you can upload anywhere
target="_blank" href="example/encrypted/example.html">example</a>). (see <a target="_blank" href="example/encrypted/example.html">example</a>).
</p> </p>
<p> <p>
The tool is also available as <a href="https://npmjs.com/package/staticrypt">a CLI on NPM</a> and is <a The tool is also available as
href="https://github.com/robinmoisson/staticrypt">open source on GitHub</a>. <a href="https://npmjs.com/package/staticrypt">a CLI on NPM</a> and is
<a href="https://github.com/robinmoisson/staticrypt">open source on GitHub</a>.
</p> </p>
<br> <br />
<h4> <h4>
<a class="no-style" id="toggle-concept" href="#"> <a class="no-style" id="toggle-concept" href="#">
@ -61,27 +75,36 @@
</h4> </h4>
<div id="concept" class="hidden"> <div id="concept" class="hidden">
<p> <p>
<b class="text-danger">Disclaimer</b> if you are an at-risk activist, or have extra sensitive <b class="text-danger">Disclaimer</b> if you are an at-risk activist, or have extra
banking data, you should probably use something else! sensitive banking data, you should probably use something else!
</p> </p>
<p> <p>
StatiCrypt generates a static, password protected page that can be decrypted in-browser: StatiCrypt generates a static, password protected page that can be decrypted in-browser:
just send or upload the generated page to a place serving static content (github pages, for example) just send or upload the generated page to a place serving static content (github pages, for
and you're done: the javascript will prompt users for password, decrypt the page and load your HTML. example) and you're done: the javascript will prompt users for password, decrypt the page
and load your HTML.
</p> </p>
<p> <p>
The page is encrypted with AES-256 in CBC mode (see why this mode is appropriate for StatiCrypt in The page is encrypted with AES-256 in CBC mode (see why this mode is appropriate for
<a href="https://github.com/robinmoisson/staticrypt/issues/19">#19</a>). The password is hashed with StatiCrypt in
PBKDF2 (599k iterations with SHA-256, plus 1k with SHA-1 for legacy reasons (see <a href="https://github.com/robinmoisson/staticrypt/issues/19">#19</a>). The password is
hashed with PBKDF2 (599k iterations with SHA-256, plus 1k with SHA-1 for legacy reasons (see
<a href="https://github.com/robinmoisson/staticrypt/issues/159">#159</a>), for the added <a href="https://github.com/robinmoisson/staticrypt/issues/159">#159</a>), for the added
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2">recommended <a
total</a> of 600k) and used to encrypt the page. href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2"
>recommended total</a
>
of 600k) and used to encrypt the page.
</p> </p>
<p> <p>
It basically encrypts your page and puts everything with a user-friendly way to use a password It basically encrypts your page and puts everything with a user-friendly way to use a
in the new file. AES-256 is state of the art but <b>brute-force/dictionary attacks would be easy to password in the new file. AES-256 is state of the art but
do at a really fast pace: use a long, unusual password!</b> <b
<br/> => To be safe, we recommend 16+ alphanum characters, and using a password manager like the >brute-force/dictionary attacks would be easy to do at a really fast pace: use a long,
unusual password!</b
>
<br />
=> To be safe, we recommend 16+ alphanum characters, and using a password manager like the
open-source <a href="http://bitwarden.com">Bitwarden</a>. open-source <a href="http://bitwarden.com">Bitwarden</a>.
</p> </p>
<p> <p>
@ -89,7 +112,7 @@
<a href="https://github.com/robinmoisson/staticrypt">GitHub project</a>. <a href="https://github.com/robinmoisson/staticrypt">GitHub project</a>.
</p> </p>
</div> </div>
<br> <br />
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@ -97,25 +120,34 @@
<form id="encrypt_form"> <form id="encrypt_form">
<div class="form-group"> <div class="form-group">
<label for="password">Password</label> <label for="password">Password</label>
<input type="password" class="form-control" id="password" <input
placeholder="Password (choose a long one!)"> type="password"
class="form-control"
id="password"
placeholder="Password (choose a long one!)"
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="unencrypted_html">HTML/string to encrypt</label> <label for="unencrypted_html">HTML/string to encrypt</label>
<textarea class="form-control" <textarea
class="form-control"
id="unencrypted_html" id="unencrypted_html"
placeholder="<html><head>..." placeholder="<html><head>..."
rows="5"></textarea> rows="5"
></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="no-style"> <label class="no-style">
<input type="checkbox" id="remember" checked> <input type="checkbox" id="remember" checked />
Add "Remember me" checkbox (append <code>#staticrypt_logout</code> to your URL to logout) Add "Remember me" checkbox (append <code>#staticrypt_logout</code> to your URL to
logout)
<small> <small>
<abbr class="text-muted" <abbr
title="The password will be stored in clear text in the browser's localStorage upon entry by the user. See &quot;More options&quot; to set the expiration (default: none)"> class="text-muted"
title='The password will be stored in clear text in the browser&apos;s localStorage upon entry by the user. See "More options" to set the expiration (default: none)'
>
(?) (?)
</abbr> </abbr>
</small> </small>
@ -128,45 +160,72 @@
<div id="extra-options" class="hidden"> <div id="extra-options" class="hidden">
<div class="form-group"> <div class="form-group">
<label for="template_title">Page title</label> <label for="template_title">Page title</label>
<input type="text" class="form-control" id="template_title" placeholder="Default: 'Protected Page'"> <input
type="text"
class="form-control"
id="template_title"
placeholder="Default: 'Protected Page'"
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="template_instructions">Instructions to display the user</label> <label for="template_instructions">Instructions to display the user</label>
<textarea class="form-control" id="template_instructions" placeholder="Default: nothing."></textarea> <textarea
class="form-control"
id="template_instructions"
placeholder="Default: nothing."
></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="template_placeholder">Password input placeholder</label> <label for="template_placeholder">Password input placeholder</label>
<input type="text" class="form-control" id="template_placeholder" <input
placeholder="Default: 'Password'"> type="text"
class="form-control"
id="template_placeholder"
placeholder="Default: 'Password'"
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="template_remember">"Remember me" checkbox label</label> <label for="template_remember">"Remember me" checkbox label</label>
<input type="text" class="form-control" id="template_remember" placeholder="Default: 'Remember me'"> <input
type="text"
class="form-control"
id="template_remember"
placeholder="Default: 'Remember me'"
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="remember_in_days">"Remember me" expiration in days</label> <label for="remember_in_days">"Remember me" expiration in days</label>
<input type="number" <input
type="number"
class="form-control" class="form-control"
id="remember_in_days" id="remember_in_days"
step="any" step="any"
placeholder="Default: 0 (no expiration)"> placeholder="Default: 0 (no expiration)"
/>
<small class="form-text text-muted"> <small class="form-text text-muted">
After this many days, the user will have to enter the password again. Leave empty or set After this many days, the user will have to enter the password again. Leave empty or
to 0 for no expiration. set to 0 for no expiration.
</small> </small>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="template_button">Decrypt button label</label> <label for="template_button">Decrypt button label</label>
<input type="text" class="form-control" id="template_button" placeholder="Default: 'DECRYPT'"> <input
type="text"
class="form-control"
id="template_button"
placeholder="Default: 'DECRYPT'"
/>
</div> </div>
</div> </div>
<button class="btn btn-primary pull-right" type="submit">Generate password protected HTML</button> <button class="btn btn-primary pull-right" type="submit">
Generate password protected HTML
</button>
</form> </form>
</div> </div>
</div> </div>
@ -174,12 +233,16 @@
<div class="row mb-5"> <div class="row mb-5">
<div class="col-xs-12"> <div class="col-xs-12">
<h2>Encrypted HTML</h2> <h2>Encrypted HTML</h2>
<p><a class="btn btn-success download" <p>
<a
class="btn btn-success download"
download="encrypted.html" download="encrypted.html"
id="download-link" id="download-link"
disabled="disabled">Download html file with password prompt</a></p> disabled="disabled"
<pre id="encrypted_html_display"> >Download html file with password prompt</a
Your encrypted string</pre> >
</p>
<pre id="encrypted_html_display">Your encrypted string</pre>
</div> </div>
</div> </div>
</div> </div>
@ -259,13 +322,7 @@ async function encrypt(msg, hashedPassword) {
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters
const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8)); const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8));
const key = await subtle.importKey( const key = await subtle.importKey("raw", HexEncoder.parse(hashedPassword), ENCRYPTION_ALGO, false, ["encrypt"]);
"raw",
HexEncoder.parse(hashedPassword),
ENCRYPTION_ALGO,
false,
["encrypt"]
);
const encrypted = await subtle.encrypt( const encrypted = await subtle.encrypt(
{ {
@ -293,13 +350,7 @@ async function decrypt(encryptedMsg, hashedPassword) {
const iv = HexEncoder.parse(encryptedMsg.substring(0, ivLength)); const iv = HexEncoder.parse(encryptedMsg.substring(0, ivLength));
const encrypted = encryptedMsg.substring(ivLength); const encrypted = encryptedMsg.substring(ivLength);
const key = await subtle.importKey( const key = await subtle.importKey("raw", HexEncoder.parse(hashedPassword), ENCRYPTION_ALGO, false, ["decrypt"]);
"raw",
HexEncoder.parse(hashedPassword),
ENCRYPTION_ALGO,
false,
["decrypt"]
);
const outBuffer = await subtle.decrypt( const outBuffer = await subtle.decrypt(
{ {
@ -381,13 +432,7 @@ exports.hashThirdRound = hashThirdRound;
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
async function pbkdf2(password, salt, iterations, hashAlgorithm) { async function pbkdf2(password, salt, iterations, hashAlgorithm) {
const key = await subtle.importKey( const key = await subtle.importKey("raw", UTF8Encoder.parse(password), "PBKDF2", false, ["deriveBits"]);
"raw",
UTF8Encoder.parse(password),
"PBKDF2",
false,
["deriveBits"]
);
const keyBytes = await subtle.deriveBits( const keyBytes = await subtle.deriveBits(
{ {
@ -427,7 +472,6 @@ async function signMessage(hashedPassword, message) {
} }
exports.signMessage = signMessage; exports.signMessage = signMessage;
function getRandomAlphanum() { function getRandomAlphanum() {
const possibleCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const possibleCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@ -455,7 +499,7 @@ function getRandomAlphanum() {
* @returns {string} * @returns {string}
*/ */
function generateRandomString(length) { function generateRandomString(length) {
let randomString = ''; let randomString = "";
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
randomString += getRandomAlphanum(); randomString += getRandomAlphanum();
@ -465,9 +509,8 @@ function generateRandomString(length) {
} }
exports.generateRandomString = generateRandomString; exports.generateRandomString = generateRandomString;
return exports; return exports;
})()) })());
</script> </script>
<script id="codec"> <script id="codec">
@ -536,13 +579,7 @@ function init(cryptoEngine) {
* *
* @returns {Object} {success: true, decoded: string} | {success: false, message: string} * @returns {Object} {success: true, decoded: string} | {success: false, message: string}
*/ */
async function decode( async function decode(signedMsg, hashedPassword, salt, backwardCompatibleAttempt = 0, originalPassword = "") {
signedMsg,
hashedPassword,
salt,
backwardCompatibleAttempt = 0,
originalPassword = ''
) {
const encryptedHMAC = signedMsg.substring(0, 64); const encryptedHMAC = signedMsg.substring(0, 64);
const encryptedMsg = signedMsg.substring(64); const encryptedMsg = signedMsg.substring(64);
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg); const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg);
@ -578,7 +615,7 @@ function init(cryptoEngine) {
exports.init = init; exports.init = init;
return exports; return exports;
})()) })());
</script> </script>
<script id="formater"> <script id="formater">
@ -589,7 +626,7 @@ exports.init = init;
* break this comment), with the provided data. * break this comment), with the provided data.
* *
* This weird format is so that we have something that doesn't break JS parser in the template files (it understands it * This weird format is so that we have something that doesn't break JS parser in the template files (it understands it
* as '0'), so we can still use auto-formatting. * as '0'), so we can still use auto-formatting. The auto-formatter might add a space before the '0', we accept both.
* *
* @param {string} templateString * @param {string} templateString
* @param {Object} data * @param {Object} data
@ -597,12 +634,12 @@ exports.init = init;
* @returns string * @returns string
*/ */
function renderTemplate(templateString, data) { function renderTemplate(templateString, data) {
return templateString.replace(/\/\*\[\|\s*(\w+)\s*\|]\*\/0/g, function (_, key) { return templateString.replace(/\/\*\[\|\s*(\w+)\s*\|]\*\/\s*0/g, function (_, key) {
if (!data || data[key] === undefined) { if (!data || data[key] === undefined) {
return key; return key;
} }
if (typeof data[key] === 'object') { if (typeof data[key] === "object") {
return JSON.stringify(data[key]); return JSON.stringify(data[key]);
} }
@ -611,9 +648,8 @@ function renderTemplate(templateString, data) {
} }
exports.renderTemplate = renderTemplate; exports.renderTemplate = renderTemplate;
return exports; return exports;
})()) })());
</script> </script>
<script id="staticrypt"> <script id="staticrypt">
@ -691,13 +727,7 @@ async function encrypt(msg, hashedPassword) {
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters
const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8)); const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8));
const key = await subtle.importKey( const key = await subtle.importKey("raw", HexEncoder.parse(hashedPassword), ENCRYPTION_ALGO, false, ["encrypt"]);
"raw",
HexEncoder.parse(hashedPassword),
ENCRYPTION_ALGO,
false,
["encrypt"]
);
const encrypted = await subtle.encrypt( const encrypted = await subtle.encrypt(
{ {
@ -725,13 +755,7 @@ async function decrypt(encryptedMsg, hashedPassword) {
const iv = HexEncoder.parse(encryptedMsg.substring(0, ivLength)); const iv = HexEncoder.parse(encryptedMsg.substring(0, ivLength));
const encrypted = encryptedMsg.substring(ivLength); const encrypted = encryptedMsg.substring(ivLength);
const key = await subtle.importKey( const key = await subtle.importKey("raw", HexEncoder.parse(hashedPassword), ENCRYPTION_ALGO, false, ["decrypt"]);
"raw",
HexEncoder.parse(hashedPassword),
ENCRYPTION_ALGO,
false,
["decrypt"]
);
const outBuffer = await subtle.decrypt( const outBuffer = await subtle.decrypt(
{ {
@ -813,13 +837,7 @@ exports.hashThirdRound = hashThirdRound;
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
async function pbkdf2(password, salt, iterations, hashAlgorithm) { async function pbkdf2(password, salt, iterations, hashAlgorithm) {
const key = await subtle.importKey( const key = await subtle.importKey("raw", UTF8Encoder.parse(password), "PBKDF2", false, ["deriveBits"]);
"raw",
UTF8Encoder.parse(password),
"PBKDF2",
false,
["deriveBits"]
);
const keyBytes = await subtle.deriveBits( const keyBytes = await subtle.deriveBits(
{ {
@ -859,7 +877,6 @@ async function signMessage(hashedPassword, message) {
} }
exports.signMessage = signMessage; exports.signMessage = signMessage;
function getRandomAlphanum() { function getRandomAlphanum() {
const possibleCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const possibleCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@ -887,7 +904,7 @@ function getRandomAlphanum() {
* @returns {string} * @returns {string}
*/ */
function generateRandomString(length) { function generateRandomString(length) {
let randomString = ''; let randomString = "";
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
randomString += getRandomAlphanum(); randomString += getRandomAlphanum();
@ -897,9 +914,8 @@ function generateRandomString(length) {
} }
exports.generateRandomString = generateRandomString; exports.generateRandomString = generateRandomString;
return exports; return exports;
})()) })());
const codec = ((function(){ const codec = ((function(){
const exports = {}; const exports = {};
/** /**
@ -965,13 +981,7 @@ function init(cryptoEngine) {
* *
* @returns {Object} {success: true, decoded: string} | {success: false, message: string} * @returns {Object} {success: true, decoded: string} | {success: false, message: string}
*/ */
async function decode( async function decode(signedMsg, hashedPassword, salt, backwardCompatibleAttempt = 0, originalPassword = "") {
signedMsg,
hashedPassword,
salt,
backwardCompatibleAttempt = 0,
originalPassword = ''
) {
const encryptedHMAC = signedMsg.substring(0, 64); const encryptedHMAC = signedMsg.substring(0, 64);
const encryptedMsg = signedMsg.substring(64); const encryptedMsg = signedMsg.substring(64);
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg); const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg);
@ -1007,10 +1017,9 @@ function init(cryptoEngine) {
exports.init = init; exports.init = init;
return exports; return exports;
})()) })());
const decode = codec.init(cryptoEngine).decode; const decode = codec.init(cryptoEngine).decode;
/** /**
* Initialize the staticrypt module, that exposes functions callbable by the password_template. * Initialize the staticrypt module, that exposes functions callbable by the password_template.
* *
@ -1048,7 +1057,7 @@ function init(staticryptConfig, templateConfig) {
const plainHTML = result.decoded; const plainHTML = result.decoded;
// if the user configured a callback call it, otherwise just replace the whole HTML // if the user configured a callback call it, otherwise just replace the whole HTML
if (typeof replaceHtmlCallback === 'function') { if (typeof replaceHtmlCallback === "function") {
replaceHtmlCallback(plainHTML); replaceHtmlCallback(plainHTML);
} else { } else {
document.write(plainHTML); document.write(plainHTML);
@ -1109,7 +1118,7 @@ function init(staticryptConfig, templateConfig) {
function clearLocalStorage() { function clearLocalStorage() {
const { clearLocalStorageCallback, rememberExpirationKey, rememberPassphraseKey } = templateConfig; const { clearLocalStorageCallback, rememberExpirationKey, rememberPassphraseKey } = templateConfig;
if (typeof clearLocalStorageCallback === 'function') { if (typeof clearLocalStorageCallback === "function") {
clearLocalStorageCallback(); clearLocalStorageCallback();
} else { } else {
localStorage.removeItem(rememberPassphraseKey); localStorage.removeItem(rememberPassphraseKey);
@ -1221,15 +1230,16 @@ function init(staticryptConfig, templateConfig) {
return exports; return exports;
} }
exports.init = init; exports.init = init;
return exports; return exports;
})()) })());
</script> </script>
<script> <script>
const encode = codec.init(cryptoEngine).encode; const encode = codec.init(cryptoEngine).encode;
// enable CKEDIRTOR // enable CKEDIRTOR
CKEDITOR.replace('template_instructions'); CKEDITOR.replace("template_instructions");
let htmlToDownload; let htmlToDownload;
@ -1240,8 +1250,7 @@ exports.init = init;
* @returns {string} * @returns {string}
*/ */
function getScriptAsString(id) { function getScriptAsString(id) {
return document.getElementById(id) return document.getElementById(id).innerText.replace(/window\.\w+ = /, "");
.innerText.replace(/window\.\w+ = /, '');
} }
/** /**
@ -1252,15 +1261,21 @@ exports.init = init;
*/ */
function trackEvent(action) { function trackEvent(action) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://zlgpaemmniviswibzuwt.supabase.co/rest/v1/rpc/increment_analytics', true); xhr.open("POST", "https://zlgpaemmniviswibzuwt.supabase.co/rest/v1/rpc/increment_analytics", true);
xhr.setRequestHeader('Content-type', 'application/json; charset=UTF-8') xhr.setRequestHeader("Content-type", "application/json; charset=UTF-8");
xhr.setRequestHeader('apikey', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpsZ3BhZW1tbml2aXN3aWJ6dXd0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NjkxMjM0OTcsImV4cCI6MTk4NDY5OTQ5N30.wNoVDHG7F6INx-IPotMs3fL1nudfaF2qvQDgG-1PhNI') xhr.setRequestHeader(
xhr.setRequestHeader('Authorization', 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpsZ3BhZW1tbml2aXN3aWJ6dXd0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NjkxMjM0OTcsImV4cCI6MTk4NDY5OTQ5N30.wNoVDHG7F6INx-IPotMs3fL1nudfaF2qvQDgG-1PhNI') "apikey",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpsZ3BhZW1tbml2aXN3aWJ6dXd0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NjkxMjM0OTcsImV4cCI6MTk4NDY5OTQ5N30.wNoVDHG7F6INx-IPotMs3fL1nudfaF2qvQDgG-1PhNI"
);
xhr.setRequestHeader(
"Authorization",
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InpsZ3BhZW1tbml2aXN3aWJ6dXd0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NjkxMjM0OTcsImV4cCI6MTk4NDY5OTQ5N30.wNoVDHG7F6INx-IPotMs3fL1nudfaF2qvQDgG-1PhNI"
);
xhr.send( xhr.send(
JSON.stringify({ JSON.stringify({
action_input: action action_input: action,
} })
)); );
} }
/** /**
@ -1269,13 +1284,13 @@ exports.init = init;
*/ */
function setFileToDownload(data) { function setFileToDownload(data) {
const request = new XMLHttpRequest(); const request = new XMLHttpRequest();
request.open('GET', 'lib/password_template.html', true); request.open("GET", "lib/password_template.html", true);
request.onload = function () { request.onload = function () {
const renderedTmpl = formater.renderTemplate(request.responseText, data); const renderedTmpl = formater.renderTemplate(request.responseText, data);
const downloadLink = document.querySelector('a.download'); const downloadLink = document.querySelector("a.download");
downloadLink.href = 'data:text/html,' + encodeURIComponent(renderedTmpl); downloadLink.href = "data:text/html," + encodeURIComponent(renderedTmpl);
downloadLink.removeAttribute('disabled'); downloadLink.removeAttribute("disabled");
htmlToDownload = renderedTmpl; htmlToDownload = renderedTmpl;
}; };
@ -1284,34 +1299,34 @@ exports.init = init;
// register page load // register page load
window.onload = function () { window.onload = function () {
trackEvent('show_index'); trackEvent("show_index");
}; };
/** /**
* Handle form submission. * Handle form submission.
*/ */
document.getElementById('encrypt_form').addEventListener('submit', async function (e) { document.getElementById("encrypt_form").addEventListener("submit", async function (e) {
e.preventDefault(); e.preventDefault();
trackEvent('generate_encrypted'); trackEvent("generate_encrypted");
// update instruction textarea value with CKEDITOR content // update instruction textarea value with CKEDITOR content
// (see https://stackoverflow.com/questions/3147670/ckeditor-update-textarea) // (see https://stackoverflow.com/questions/3147670/ckeditor-update-textarea)
CKEDITOR.instances['template_instructions'].updateElement(); CKEDITOR.instances["template_instructions"].updateElement();
const unencrypted = document.getElementById('unencrypted_html').value, const unencrypted = document.getElementById("unencrypted_html").value,
password = document.getElementById('password').value; password = document.getElementById("password").value;
const salt = cryptoEngine.generateRandomSalt(); const salt = cryptoEngine.generateRandomSalt();
const encryptedMsg = await encode(unencrypted, password, salt); const encryptedMsg = await encode(unencrypted, password, salt);
const templateButton = document.getElementById('template_button').value, const templateButton = document.getElementById("template_button").value,
templateInstructions = document.getElementById('template_instructions').value, templateInstructions = document.getElementById("template_instructions").value,
isRememberEnabled = document.getElementById('remember').checked, isRememberEnabled = document.getElementById("remember").checked,
templateTitle = document.getElementById('template_title').value.trim(), templateTitle = document.getElementById("template_title").value.trim(),
templatePlaceholder = document.getElementById('template_placeholder').value.trim(), templatePlaceholder = document.getElementById("template_placeholder").value.trim(),
rememberDurationInDays = document.getElementById('remember_in_days').value || 0, rememberDurationInDays = document.getElementById("remember_in_days").value || 0,
templateRemember = document.getElementById('template_remember').value; templateRemember = document.getElementById("template_remember").value;
const data = { const data = {
staticrypt_config: { staticrypt_config: {
@ -1321,61 +1336,56 @@ exports.init = init;
salt, salt,
}, },
is_remember_enabled: JSON.stringify(isRememberEnabled), is_remember_enabled: JSON.stringify(isRememberEnabled),
js_staticrypt: getScriptAsString('staticrypt'), js_staticrypt: getScriptAsString("staticrypt"),
template_button: templateButton ? templateButton : 'DECRYPT', template_button: templateButton ? templateButton : "DECRYPT",
template_instructions: templateInstructions || '', template_instructions: templateInstructions || "",
template_placeholder: templatePlaceholder || 'Password', template_placeholder: templatePlaceholder || "Password",
template_remember: templateRemember || 'Remember me', template_remember: templateRemember || "Remember me",
template_title: templateTitle || 'Protected Page', template_title: templateTitle || "Protected Page",
}; };
document.getElementById('encrypted_html_display').textContent = encryptedMsg; document.getElementById("encrypted_html_display").textContent = encryptedMsg;
setFileToDownload(data); setFileToDownload(data);
}); });
document.getElementById('toggle-extra-option') document.getElementById("toggle-extra-option").addEventListener("click", function (e) {
.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
document.getElementById('extra-options').classList.toggle('hidden'); document.getElementById("extra-options").classList.toggle("hidden");
}); });
let isConceptShown = false; let isConceptShown = false;
document.getElementById('toggle-concept') document.getElementById("toggle-concept").addEventListener("click", function (e) {
.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
isConceptShown = !isConceptShown; isConceptShown = !isConceptShown;
document.getElementById('toggle-concept-sign').innerText = isConceptShown ? '▼' : '►'; document.getElementById("toggle-concept-sign").innerText = isConceptShown ? "▼" : "►";
document.getElementById('concept').classList.toggle('hidden'); document.getElementById("concept").classList.toggle("hidden");
}); });
/** /**
* Browser specific download code. * Browser specific download code.
*/ */
document.getElementById('download-link') document.getElementById("download-link").addEventListener("click", function (e) {
.addEventListener('click', function (e) {
// only register the click event if there is actually a generated file // only register the click event if there is actually a generated file
if (htmlToDownload) { if (htmlToDownload) {
trackEvent('download_encrypted'); trackEvent("download_encrypted");
} }
const isIE = (navigator.userAgent.indexOf("MSIE") !== -1) || (!!document.documentMode === true); // >= 10 const isIE = navigator.userAgent.indexOf("MSIE") !== -1 || !!document.documentMode === true; // >= 10
const isEdge = navigator.userAgent.indexOf("Edge") !== -1; const isEdge = navigator.userAgent.indexOf("Edge") !== -1;
// download with MS specific feature // download with MS specific feature
if (htmlToDownload && (isIE || isEdge)) { if (htmlToDownload && (isIE || isEdge)) {
e.preventDefault(); e.preventDefault();
const blobObject = new Blob([htmlToDownload]); const blobObject = new Blob([htmlToDownload]);
window.navigator.msSaveOrOpenBlob(blobObject, 'encrypted.html'); window.navigator.msSaveOrOpenBlob(blobObject, "encrypted.html");
} }
return true; return true;
}) });
</script> </script>
</body> </body>
</html> </html>