|
<!DOCTYPE html> |
|
<html class="staticrypt-html"> |
|
<head> |
|
<meta charset="utf-8" /> |
|
<title>Protected Page</title> |
|
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
|
|
|
|
|
<meta http-equiv="cache-control" content="max-age=0" /> |
|
<meta http-equiv="cache-control" content="no-cache" /> |
|
<meta http-equiv="expires" content="0" /> |
|
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" /> |
|
<meta http-equiv="pragma" content="no-cache" /> |
|
|
|
<style> |
|
.staticrypt-hr { |
|
margin-top: 20px; |
|
margin-bottom: 20px; |
|
border: 0; |
|
border-top: 1px solid #eee; |
|
} |
|
|
|
.staticrypt-page { |
|
width: 360px; |
|
padding: 8% 0 0; |
|
margin: auto; |
|
box-sizing: border-box; |
|
} |
|
|
|
.staticrypt-form { |
|
position: relative; |
|
z-index: 1; |
|
background: #ffffff; |
|
max-width: 360px; |
|
margin: 0 auto 100px; |
|
padding: 45px; |
|
text-align: center; |
|
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24); |
|
} |
|
|
|
.staticrypt-form input[type="password"], |
|
input[type="text"] { |
|
background: inherit; |
|
border: 0; |
|
box-sizing: border-box; |
|
font-size: 14px; |
|
outline: 0; |
|
padding: 15px 30px 15px 15px; |
|
width: 100%; |
|
} |
|
|
|
.staticrypt-password-container { |
|
position: relative; |
|
outline: 0; |
|
background: #f2f2f2; |
|
width: 100%; |
|
border: 0; |
|
margin: 0 0 15px; |
|
box-sizing: border-box; |
|
} |
|
|
|
.staticrypt-toggle-password-visibility { |
|
cursor: pointer; |
|
height: 20px; |
|
opacity: 60%; |
|
padding: 13px; |
|
position: absolute; |
|
right: 0; |
|
top: 50%; |
|
transform: translateY(-50%); |
|
width: 20px; |
|
} |
|
|
|
.staticrypt-form .staticrypt-decrypt-button { |
|
text-transform: uppercase; |
|
outline: 0; |
|
background: #4CAF50; |
|
width: 100%; |
|
border: 0; |
|
padding: 15px; |
|
color: #ffffff; |
|
font-size: 14px; |
|
cursor: pointer; |
|
} |
|
|
|
.staticrypt-form .staticrypt-decrypt-button:hover, |
|
.staticrypt-form .staticrypt-decrypt-button:active, |
|
.staticrypt-form .staticrypt-decrypt-button:focus { |
|
background: #4CAF50; |
|
filter: brightness(92%); |
|
} |
|
|
|
.staticrypt-html { |
|
height: 100%; |
|
} |
|
|
|
.staticrypt-body { |
|
height: 100%; |
|
margin: 0; |
|
} |
|
|
|
.staticrypt-content { |
|
height: 100%; |
|
margin-bottom: 1em; |
|
background: #76B852; |
|
font-family: "Arial", sans-serif; |
|
-webkit-font-smoothing: antialiased; |
|
-moz-osx-font-smoothing: grayscale; |
|
} |
|
|
|
.staticrypt-instructions { |
|
margin-top: -1em; |
|
margin-bottom: 1em; |
|
} |
|
|
|
.staticrypt-title { |
|
font-size: 1.5em; |
|
} |
|
|
|
label.staticrypt-remember { |
|
display: flex; |
|
align-items: center; |
|
margin-bottom: 1em; |
|
} |
|
|
|
.staticrypt-remember input[type="checkbox"] { |
|
transform: scale(1.5); |
|
margin-right: 1em; |
|
} |
|
|
|
.hidden { |
|
display: none !important; |
|
} |
|
|
|
.staticrypt-spinner-container { |
|
height: 100%; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
} |
|
|
|
.staticrypt-spinner { |
|
display: inline-block; |
|
width: 2rem; |
|
height: 2rem; |
|
vertical-align: text-bottom; |
|
border: 0.25em solid gray; |
|
border-right-color: transparent; |
|
border-radius: 50%; |
|
-webkit-animation: spinner-border 0.75s linear infinite; |
|
animation: spinner-border 0.75s linear infinite; |
|
animation-duration: 0.75s; |
|
animation-timing-function: linear; |
|
animation-delay: 0s; |
|
animation-iteration-count: infinite; |
|
animation-direction: normal; |
|
animation-fill-mode: none; |
|
animation-play-state: running; |
|
animation-name: spinner-border; |
|
} |
|
|
|
@keyframes spinner-border { |
|
100% { |
|
transform: rotate(360deg); |
|
} |
|
} |
|
|
|
@media screen and (-webkit-min-device-pixel-ratio: 0) { |
|
.staticrypt-form input[type="password"], |
|
input[type="text"] { |
|
font-size: 16px; |
|
} |
|
} |
|
</style> |
|
</head> |
|
|
|
<body class="staticrypt-body"> |
|
<div id="staticrypt_loading" class="staticrypt-spinner-container"> |
|
<div class="staticrypt-spinner"></div> |
|
</div> |
|
|
|
<div id="staticrypt_content" class="staticrypt-content hidden"> |
|
<div class="staticrypt-page"> |
|
<div class="staticrypt-form"> |
|
<div class="staticrypt-instructions"> |
|
<p class="staticrypt-title">Protected Page</p> |
|
<p></p> |
|
</div> |
|
|
|
<hr class="staticrypt-hr" /> |
|
|
|
<form id="staticrypt-form" action="#" method="post"> |
|
<div class="staticrypt-password-container"> |
|
<input |
|
id="staticrypt-password" |
|
type="password" |
|
name="password" |
|
placeholder="Password" |
|
autofocus |
|
/> |
|
|
|
<img |
|
class="staticrypt-toggle-password-visibility" |
|
alt="template_toggle_show" |
|
title="template_toggle_show" |
|
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4LjggNS4xQzI4LjQtMy4xIDEzLjMtMS4yIDUuMSA5LjJTLTEuMiAzNC43IDkuMiA0Mi45bDU5MiA0NjRjMTAuNCA4LjIgMjUuNSA2LjMgMzMuNy00LjFzNi4zLTI1LjUtNC4xLTMzLjdMNTI1LjYgMzg2LjdjMzkuNi00MC42IDY2LjQtODYuMSA3OS45LTExOC40YzMuMy03LjkgMy4zLTE2LjcgMC0yNC42Yy0xNC45LTM1LjctNDYuMi04Ny43LTkzLTEzMS4xQzQ2NS41IDY4LjggNDAwLjggMzIgMzIwIDMyYy02OC4yIDAtMTI1IDI2LjMtMTY5LjMgNjAuOEwzOC44IDUuMXpNMjIzLjEgMTQ5LjVDMjQ4LjYgMTI2LjIgMjgyLjcgMTEyIDMyMCAxMTJjNzkuNSAwIDE0NCA2NC41IDE0NCAxNDRjMCAyNC45LTYuMyA0OC4zLTE3LjQgNjguN0w0MDggMjk0LjVjOC40LTE5LjMgMTAuNi00MS40IDQuOC02My4zYy0xMS4xLTQxLjUtNDcuOC02OS40LTg4LjYtNzEuMWMtNS44LS4yLTkuMiA2LjEtNy40IDExLjdjMi4xIDYuNCAzLjMgMTMuMiAzLjMgMjAuM2MwIDEwLjItMi40IDE5LjgtNi42IDI4LjNsLTkwLjMtNzAuOHpNMzczIDM4OS45Yy0xNi40IDYuNS0zNC4zIDEwLjEtNTMgMTAuMWMtNzkuNSAwLTE0NC02NC41LTE0NC0xNDRjMC02LjkgLjUtMTMuNiAxLjQtMjAuMkw4My4xIDE2MS41QzYwLjMgMTkxLjIgNDQgMjIwLjggMzQuNSAyNDMuN2MtMy4zIDcuOS0zLjMgMTYuNyAwIDI0LjZjMTQuOSAzNS43IDQ2LjIgODcuNyA5MyAxMzEuMUMxNzQuNSA0NDMuMiAyMzkuMiA0ODAgMzIwIDQ4MGM0Ny44IDAgODkuOS0xMi45IDEyNi4yLTMyLjVMMzczIDM4OS45eiIvPjwvc3ZnPg==" |
|
/> |
|
</div> |
|
|
|
<label id="staticrypt-remember-label" class="staticrypt-remember hidden"> |
|
<input id="staticrypt-remember" type="checkbox" name="remember" /> |
|
Remember me |
|
</label> |
|
|
|
<input type="submit" class="staticrypt-decrypt-button" value="DECRYPT" /> |
|
</form> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
const staticryptInitiator = |
|
((function(){ |
|
const exports = {}; |
|
const cryptoEngine = ((function(){ |
|
const exports = {}; |
|
const { subtle } = crypto; |
|
|
|
const IV_BITS = 16 * 8; |
|
const HEX_BITS = 4; |
|
const ENCRYPTION_ALGO = "AES-CBC"; |
|
|
|
|
|
|
|
|
|
|
|
const HexEncoder = { |
|
|
|
|
|
|
|
|
|
|
|
parse: function (hexString) { |
|
if (hexString.length % 2 !== 0) throw "Invalid hexString"; |
|
const arrayBuffer = new Uint8Array(hexString.length / 2); |
|
|
|
for (let i = 0; i < hexString.length; i += 2) { |
|
const byteValue = parseInt(hexString.substring(i, i + 2), 16); |
|
if (isNaN(byteValue)) { |
|
throw "Invalid hexString"; |
|
} |
|
arrayBuffer[i / 2] = byteValue; |
|
} |
|
return arrayBuffer; |
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
stringify: function (bytes) { |
|
const hexBytes = []; |
|
|
|
for (let i = 0; i < bytes.length; ++i) { |
|
let byteString = bytes[i].toString(16); |
|
if (byteString.length < 2) { |
|
byteString = "0" + byteString; |
|
} |
|
hexBytes.push(byteString); |
|
} |
|
return hexBytes.join(""); |
|
}, |
|
}; |
|
|
|
|
|
|
|
|
|
const UTF8Encoder = { |
|
parse: function (str) { |
|
return new TextEncoder().encode(str); |
|
}, |
|
|
|
stringify: function (bytes) { |
|
return new TextDecoder().decode(bytes); |
|
}, |
|
}; |
|
|
|
|
|
|
|
|
|
async function encrypt(msg, hashedPassword) { |
|
|
|
|
|
const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8)); |
|
|
|
const key = await subtle.importKey("raw", HexEncoder.parse(hashedPassword), ENCRYPTION_ALGO, false, ["encrypt"]); |
|
|
|
const encrypted = await subtle.encrypt( |
|
{ |
|
name: ENCRYPTION_ALGO, |
|
iv: iv, |
|
}, |
|
key, |
|
UTF8Encoder.parse(msg) |
|
); |
|
|
|
|
|
return HexEncoder.stringify(iv) + HexEncoder.stringify(new Uint8Array(encrypted)); |
|
} |
|
exports.encrypt = encrypt; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function decrypt(encryptedMsg, hashedPassword) { |
|
const ivLength = IV_BITS / HEX_BITS; |
|
const iv = HexEncoder.parse(encryptedMsg.substring(0, ivLength)); |
|
const encrypted = encryptedMsg.substring(ivLength); |
|
|
|
const key = await subtle.importKey("raw", HexEncoder.parse(hashedPassword), ENCRYPTION_ALGO, false, ["decrypt"]); |
|
|
|
const outBuffer = await subtle.decrypt( |
|
{ |
|
name: ENCRYPTION_ALGO, |
|
iv: iv, |
|
}, |
|
key, |
|
HexEncoder.parse(encrypted) |
|
); |
|
|
|
return UTF8Encoder.stringify(new Uint8Array(outBuffer)); |
|
} |
|
exports.decrypt = decrypt; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function hashPassword(password, salt) { |
|
|
|
|
|
let hashedPassword = await hashLegacyRound(password, salt); |
|
|
|
hashedPassword = await hashSecondRound(hashedPassword, salt); |
|
|
|
return hashThirdRound(hashedPassword, salt); |
|
} |
|
exports.hashPassword = hashPassword; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hashLegacyRound(password, salt) { |
|
return pbkdf2(password, salt, 1000, "SHA-1"); |
|
} |
|
exports.hashLegacyRound = hashLegacyRound; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hashSecondRound(hashedPassword, salt) { |
|
return pbkdf2(hashedPassword, salt, 14000, "SHA-256"); |
|
} |
|
exports.hashSecondRound = hashSecondRound; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hashThirdRound(hashedPassword, salt) { |
|
return pbkdf2(hashedPassword, salt, 585000, "SHA-256"); |
|
} |
|
exports.hashThirdRound = hashThirdRound; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function pbkdf2(password, salt, iterations, hashAlgorithm) { |
|
const key = await subtle.importKey("raw", UTF8Encoder.parse(password), "PBKDF2", false, ["deriveBits"]); |
|
|
|
const keyBytes = await subtle.deriveBits( |
|
{ |
|
name: "PBKDF2", |
|
hash: hashAlgorithm, |
|
iterations, |
|
salt: UTF8Encoder.parse(salt), |
|
}, |
|
key, |
|
256 |
|
); |
|
|
|
return HexEncoder.stringify(new Uint8Array(keyBytes)); |
|
} |
|
|
|
function generateRandomSalt() { |
|
const bytes = crypto.getRandomValues(new Uint8Array(128 / 8)); |
|
|
|
return HexEncoder.stringify(new Uint8Array(bytes)); |
|
} |
|
exports.generateRandomSalt = generateRandomSalt; |
|
|
|
async function signMessage(hashedPassword, message) { |
|
const key = await subtle.importKey( |
|
"raw", |
|
HexEncoder.parse(hashedPassword), |
|
{ |
|
name: "HMAC", |
|
hash: "SHA-256", |
|
}, |
|
false, |
|
["sign"] |
|
); |
|
const signature = await subtle.sign("HMAC", key, UTF8Encoder.parse(message)); |
|
|
|
return HexEncoder.stringify(new Uint8Array(signature)); |
|
} |
|
exports.signMessage = signMessage; |
|
|
|
function getRandomAlphanum() { |
|
const possibleCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
|
|
|
let byteArray; |
|
let parsedInt; |
|
|
|
|
|
|
|
do { |
|
byteArray = crypto.getRandomValues(new Uint8Array(1)); |
|
|
|
parsedInt = byteArray[0] & 0xff; |
|
} while (parsedInt >= 256 - (256 % possibleCharacters.length)); |
|
|
|
|
|
const randomIndex = parsedInt % possibleCharacters.length; |
|
|
|
return possibleCharacters[randomIndex]; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function generateRandomString(length) { |
|
let randomString = ""; |
|
|
|
for (let i = 0; i < length; i++) { |
|
randomString += getRandomAlphanum(); |
|
} |
|
|
|
return randomString; |
|
} |
|
exports.generateRandomString = generateRandomString; |
|
|
|
return exports; |
|
})()); |
|
const codec = ((function(){ |
|
const exports = {}; |
|
|
|
|
|
|
|
|
|
|
|
function init(cryptoEngine) { |
|
const exports = {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function encode(msg, password, salt) { |
|
const hashedPassword = await cryptoEngine.hashPassword(password, salt); |
|
|
|
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword); |
|
|
|
|
|
|
|
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted); |
|
|
|
return hmac + encrypted; |
|
} |
|
exports.encode = encode; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function encodeWithHashedPassword(msg, hashedPassword) { |
|
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword); |
|
|
|
|
|
|
|
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted); |
|
|
|
return hmac + encrypted; |
|
} |
|
exports.encodeWithHashedPassword = encodeWithHashedPassword; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function decode(signedMsg, hashedPassword, salt, backwardCompatibleAttempt = 0, originalPassword = "") { |
|
const encryptedHMAC = signedMsg.substring(0, 64); |
|
const encryptedMsg = signedMsg.substring(64); |
|
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg); |
|
|
|
if (decryptedHMAC !== encryptedHMAC) { |
|
|
|
|
|
originalPassword = originalPassword || hashedPassword; |
|
if (backwardCompatibleAttempt === 0) { |
|
const updatedHashedPassword = await cryptoEngine.hashThirdRound(originalPassword, salt); |
|
|
|
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword); |
|
} |
|
if (backwardCompatibleAttempt === 1) { |
|
let updatedHashedPassword = await cryptoEngine.hashSecondRound(originalPassword, salt); |
|
updatedHashedPassword = await cryptoEngine.hashThirdRound(updatedHashedPassword, salt); |
|
|
|
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword); |
|
} |
|
|
|
return { success: false, message: "Signature mismatch" }; |
|
} |
|
|
|
return { |
|
success: true, |
|
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassword), |
|
}; |
|
} |
|
exports.decode = decode; |
|
|
|
return exports; |
|
} |
|
exports.init = init; |
|
|
|
return exports; |
|
})()); |
|
const decode = codec.init(cryptoEngine).decode; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function init(staticryptConfig, templateConfig) { |
|
const exports = {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function decryptAndReplaceHtml(hashedPassword) { |
|
const { staticryptEncryptedMsgUniqueVariableName, staticryptSaltUniqueVariableName } = staticryptConfig; |
|
const { replaceHtmlCallback } = templateConfig; |
|
|
|
const result = await decode( |
|
staticryptEncryptedMsgUniqueVariableName, |
|
hashedPassword, |
|
staticryptSaltUniqueVariableName |
|
); |
|
if (!result.success) { |
|
return false; |
|
} |
|
const plainHTML = result.decoded; |
|
|
|
|
|
if (typeof replaceHtmlCallback === "function") { |
|
replaceHtmlCallback(plainHTML); |
|
} else { |
|
document.write(plainHTML); |
|
document.close(); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function handleDecryptionOfPage(password, isRememberChecked) { |
|
const { staticryptSaltUniqueVariableName } = staticryptConfig; |
|
|
|
|
|
const hashedPassword = await cryptoEngine.hashPassword(password, staticryptSaltUniqueVariableName); |
|
return handleDecryptionOfPageFromHash(hashedPassword, isRememberChecked); |
|
} |
|
exports.handleDecryptionOfPage = handleDecryptionOfPage; |
|
|
|
async function handleDecryptionOfPageFromHash(hashedPassword, isRememberChecked) { |
|
const { isRememberEnabled, rememberDurationInDays } = staticryptConfig; |
|
const { rememberExpirationKey, rememberPassphraseKey } = templateConfig; |
|
|
|
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword); |
|
|
|
if (!isDecryptionSuccessful) { |
|
return { |
|
isSuccessful: false, |
|
hashedPassword, |
|
}; |
|
} |
|
|
|
|
|
if (isRememberEnabled && isRememberChecked) { |
|
window.localStorage.setItem(rememberPassphraseKey, hashedPassword); |
|
|
|
|
|
if (rememberDurationInDays > 0) { |
|
window.localStorage.setItem( |
|
rememberExpirationKey, |
|
(new Date().getTime() + rememberDurationInDays * 24 * 60 * 60 * 1000).toString() |
|
); |
|
} |
|
} |
|
|
|
return { |
|
isSuccessful: true, |
|
hashedPassword, |
|
}; |
|
} |
|
exports.handleDecryptionOfPageFromHash = handleDecryptionOfPageFromHash; |
|
|
|
|
|
|
|
|
|
function clearLocalStorage() { |
|
const { clearLocalStorageCallback, rememberExpirationKey, rememberPassphraseKey } = templateConfig; |
|
|
|
if (typeof clearLocalStorageCallback === "function") { |
|
clearLocalStorageCallback(); |
|
} else { |
|
localStorage.removeItem(rememberPassphraseKey); |
|
localStorage.removeItem(rememberExpirationKey); |
|
} |
|
} |
|
|
|
async function handleDecryptOnLoad() { |
|
let isSuccessful = await decryptOnLoadFromUrl(); |
|
|
|
if (!isSuccessful) { |
|
isSuccessful = await decryptOnLoadFromRememberMe(); |
|
} |
|
|
|
return { isSuccessful }; |
|
} |
|
exports.handleDecryptOnLoad = handleDecryptOnLoad; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function logoutIfNeeded() { |
|
const logoutKey = "staticrypt_logout"; |
|
|
|
|
|
const queryParams = new URLSearchParams(window.location.search); |
|
if (queryParams.has(logoutKey)) { |
|
clearLocalStorage(); |
|
return true; |
|
} |
|
|
|
|
|
const hash = window.location.hash.substring(1); |
|
if (hash.includes(logoutKey)) { |
|
clearLocalStorage(); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function decryptOnLoadFromRememberMe() { |
|
const { rememberDurationInDays } = staticryptConfig; |
|
const { rememberExpirationKey, rememberPassphraseKey } = templateConfig; |
|
|
|
|
|
if (logoutIfNeeded()) { |
|
return false; |
|
} |
|
|
|
|
|
if (rememberDurationInDays && rememberDurationInDays > 0) { |
|
const expiration = localStorage.getItem(rememberExpirationKey), |
|
isExpired = expiration && new Date().getTime() > parseInt(expiration); |
|
|
|
if (isExpired) { |
|
clearLocalStorage(); |
|
return false; |
|
} |
|
} |
|
|
|
const hashedPassword = localStorage.getItem(rememberPassphraseKey); |
|
|
|
if (hashedPassword) { |
|
|
|
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword); |
|
|
|
|
|
|
|
if (!isDecryptionSuccessful) { |
|
clearLocalStorage(); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
async function decryptOnLoadFromUrl() { |
|
const passwordKey = "staticrypt_pwd"; |
|
const rememberMeKey = "remember_me"; |
|
|
|
|
|
|
|
const queryParams = new URLSearchParams(window.location.search); |
|
const hashedPasswordQuery = queryParams.get(passwordKey); |
|
const rememberMeQuery = queryParams.get(rememberMeKey); |
|
|
|
const urlFragment = window.location.hash.substring(1); |
|
|
|
const hashedPasswordRegexMatch = urlFragment.match(new RegExp(passwordKey + "=([^&]*)")); |
|
const hashedPasswordFragment = hashedPasswordRegexMatch ? hashedPasswordRegexMatch[1] : null; |
|
const rememberMeFragment = urlFragment.includes(rememberMeKey); |
|
|
|
const hashedPassword = hashedPasswordFragment || hashedPasswordQuery; |
|
const rememberMe = rememberMeFragment || rememberMeQuery; |
|
|
|
if (hashedPassword) { |
|
return handleDecryptionOfPageFromHash(hashedPassword, rememberMe); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
return exports; |
|
} |
|
exports.init = init; |
|
|
|
return exports; |
|
})()); |
|
; |
|
const templateError = "template_error", |
|
templateToggleAltShow = "template_toggle_show", |
|
templateToggleAltHide = "template_toggle_hide", |
|
isRememberEnabled = true, |
|
staticryptConfig = {"staticryptEncryptedMsgUniqueVariableName":"d8a0b8fd6f691b755bd9cddf76cde1a15cfc2e8d92a913972a1bb0f9c354cffc162701d628367c01be59718fcdef537dbc9570119143a252669a3568fa1d768084759ce8c9dfa66c4eb0e65d35f5886430aed8fd43cff7cd6b95a6b4e38dd67001f44dea9dc40a5c01d87eb0ec090f4181dffedeb5539d93d94d2ddc5309b140dea085b0a87445c13c320bb9a4a2dd6742889240095933d7e5530d1aff83c5756ee338150400beeb58fae25b11ed24d99c06694a789d6f3de052de59d3b6ec5ab9d074f723dffc885d3725bdeb85df15b4ece8d5c7d41d75e888f7a60f84b532a2b558ff3a2dd7e00d58565eb8f9e623ab3f57a118fef1a4ca57d860633bd10057f7a5353997b2ecd1dae950abc925fe11e8c9114b78a4a92db9ceea832465613296f8504ca49cd25acaf66912a16288759895970da6a00cfbe8302c8b525ba4949b800869f832401d0f9da2b3b93221c535f797c9fee8f2b0fd8e28047799257f594fe4f70331ae75223bec79bac780e6d4311f6bbe0021a3cd720e221492b70849fd1a150ab5bc09b1731c1c2be0e30d49b7def213061e5aaf0db4633a218a58d6411e40f9154d2628af7374a5a93b30c74618dc67cfa231ddbc5e3954817418ba8e357feb0bcaf8c407c77df9741619ee29e5c32df82b09a24298ed1d0e169f9665d6de1e0bddaa54eb640ac701a6b65705984386ce11d2ee3fb1565980c58fc2fdbc88802a89c699ec3736eaeb8375c0030c9248a0064f649f70580f73cb8237ad8683ff82e471fd1c8f9664a0d84876566ad15866e784c88e5b62f893f0449af62e01e88e467a95b90d3df58c15c3eaf7575613ccfcc476aec0af58eb438513e781a1a8a499b4bf877c9057c40500ab8a2e766bd484620f75e59764c2398e21e3ad287d86bd407fd4223cc8fe09d3d1f5ec360357b3c7e51e7829db1710f1421304454805cc4a75b2842125e818f763babd03d84c1b6b270fe87c4de289f2806e6ba25e17ca164608af8ae7da2efdc8099ce8d24dfbc68f572ea1f3aefe6275310b2b6f2ac2d86080243bdbaa79c9fda2a36ff3ebd770960a86450211273c9b94478f09c9e8c879d6723cadb617a7b4278bcf77e87899d472cb886ddedee73ca72412eba1fe674ca74cf6d643b51ff1d768f6394e82824e0b5f855fb533a93c4a4d110b010fcbdbadb3a56d5b258e45342dc3af21de6a95302ec0887eb2e719892f93697e1457438e080cf57a71f861ff51525af1a6d879721ec3eb6249fb50681bcf14cac112bbcb80c4fa7ea677fa70c424fefe46d738971256d02413916ed6ff223bdb861ad6cdb9f439d4842c2b9d1fafc60d2c98c4473bfa703aa12ca896f51cedfe57d28c59b8ae0310127d180ef1eb0220fd190c44590654462647cc03df5ba7f3ee307b05b3f9230c959ac8139af2b12f3c6efda3d754361405a239c99d43cc1f5649c0388c61395b6e77cbd7d7c2e4ca00c8ba54e9b32284992af341f0975ab87547c11729780eef2bb728625ece8fe8f39de5c4eb479a56b8886ae7a0a93a093e9528930ab8ae156ea9da8d1656546926df55c30473b18b64b4fbb71b9b470ccdcf653da1eb0bfd194e2de5ac957bd8dea7090bf53b5848c1bf75bfd9a7caa3ccd75013ee21fb9a8f0eb0023abbb966921f2dc529c9466916575e56137fb0b7b6ba7018074ed5f3ca70965acb30b6140aff2bf67d3c0c8f7a34d5cdadc051e9d3f571f830277ea8313e33d95851f01c5cdb6cd66ddf093f608d488c1fc5259a7d1bd8dd13443ebb33ffbcff9cb10c6861b2a5d42983779e5b","isRememberEnabled":true,"rememberDurationInDays":0,"staticryptSaltUniqueVariableName":"5367f033edb0d1a68653eaf7da5935c6"}; |
|
|
|
|
|
const templateConfig = { |
|
rememberExpirationKey: "staticrypt_expiration", |
|
rememberPassphraseKey: "staticrypt_passphrase", |
|
replaceHtmlCallback: null, |
|
clearLocalStorageCallback: null, |
|
}; |
|
|
|
|
|
const staticrypt = staticryptInitiator.init(staticryptConfig, templateConfig); |
|
|
|
|
|
window.onload = async function () { |
|
const { isSuccessful } = await staticrypt.handleDecryptOnLoad(); |
|
|
|
|
|
|
|
if (!isSuccessful) { |
|
|
|
document.getElementById("staticrypt_loading").classList.add("hidden"); |
|
document.getElementById("staticrypt_content").classList.remove("hidden"); |
|
document.getElementById("staticrypt-password").focus(); |
|
|
|
|
|
if (isRememberEnabled) { |
|
document.getElementById("staticrypt-remember-label").classList.remove("hidden"); |
|
} |
|
} |
|
}; |
|
|
|
|
|
const toggleIcon = document.querySelector(".staticrypt-toggle-password-visibility"); |
|
|
|
const imgSrcEyeClosed = |
|
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTM4LjggNS4xQzI4LjQtMy4xIDEzLjMtMS4yIDUuMSA5LjJTLTEuMiAzNC43IDkuMiA0Mi45bDU5MiA0NjRjMTAuNCA4LjIgMjUuNSA2LjMgMzMuNy00LjFzNi4zLTI1LjUtNC4xLTMzLjdMNTI1LjYgMzg2LjdjMzkuNi00MC42IDY2LjQtODYuMSA3OS45LTExOC40YzMuMy03LjkgMy4zLTE2LjcgMC0yNC42Yy0xNC45LTM1LjctNDYuMi04Ny43LTkzLTEzMS4xQzQ2NS41IDY4LjggNDAwLjggMzIgMzIwIDMyYy02OC4yIDAtMTI1IDI2LjMtMTY5LjMgNjAuOEwzOC44IDUuMXpNMjIzLjEgMTQ5LjVDMjQ4LjYgMTI2LjIgMjgyLjcgMTEyIDMyMCAxMTJjNzkuNSAwIDE0NCA2NC41IDE0NCAxNDRjMCAyNC45LTYuMyA0OC4zLTE3LjQgNjguN0w0MDggMjk0LjVjOC40LTE5LjMgMTAuNi00MS40IDQuOC02My4zYy0xMS4xLTQxLjUtNDcuOC02OS40LTg4LjYtNzEuMWMtNS44LS4yLTkuMiA2LjEtNy40IDExLjdjMi4xIDYuNCAzLjMgMTMuMiAzLjMgMjAuM2MwIDEwLjItMi40IDE5LjgtNi42IDI4LjNsLTkwLjMtNzAuOHpNMzczIDM4OS45Yy0xNi40IDYuNS0zNC4zIDEwLjEtNTMgMTAuMWMtNzkuNSAwLTE0NC02NC41LTE0NC0xNDRjMC02LjkgLjUtMTMuNiAxLjQtMjAuMkw4My4xIDE2MS41QzYwLjMgMTkxLjIgNDQgMjIwLjggMzQuNSAyNDMuN2MtMy4zIDcuOS0zLjMgMTYuNyAwIDI0LjZjMTQuOSAzNS43IDQ2LjIgODcuNyA5MyAxMzEuMUMxNzQuNSA0NDMuMiAyMzkuMiA0ODAgMzIwIDQ4MGM0Ny44IDAgODkuOS0xMi45IDEyNi4yLTMyLjVMMzczIDM4OS45eiIvPjwvc3ZnPg=="; |
|
const imgSrcEyeOpened = |
|
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNS4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjQgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTI4OCAzMmMtODAuOCAwLTE0NS41IDM2LjgtMTkyLjYgODAuNkM0OC42IDE1NiAxNy4zIDIwOCAyLjUgMjQzLjdjLTMuMyA3LjktMy4zIDE2LjcgMCAyNC42QzE3LjMgMzA0IDQ4LjYgMzU2IDk1LjQgMzk5LjRDMTQyLjUgNDQzLjIgMjA3LjIgNDgwIDI4OCA0ODBzMTQ1LjUtMzYuOCAxOTIuNi04MC42YzQ2LjgtNDMuNSA3OC4xLTk1LjQgOTMtMTMxLjFjMy4zLTcuOSAzLjMtMTYuNyAwLTI0LjZjLTE0LjktMzUuNy00Ni4yLTg3LjctOTMtMTMxLjFDNDMzLjUgNjguOCAzNjguOCAzMiAyODggMzJ6TTE0NCAyNTZhMTQ0IDE0NCAwIDEgMSAyODggMCAxNDQgMTQ0IDAgMSAxIC0yODggMHptMTQ0LTY0YzAgMzUuMy0yOC43IDY0LTY0IDY0Yy03LjEgMC0xMy45LTEuMi0yMC4zLTMuM2MtNS41LTEuOC0xMS45IDEuNi0xMS43IDcuNGMuMyA2LjkgMS4zIDEzLjggMy4yIDIwLjdjMTMuNyA1MS4yIDY2LjQgODEuNiAxMTcuNiA2Ny45czgxLjYtNjYuNCA2Ny45LTExNy42Yy0xMS4xLTQxLjUtNDcuOC02OS40LTg4LjYtNzEuMWMtNS44LS4yLTkuMiA2LjEtNy40IDExLjdjMi4xIDYuNCAzLjMgMTMuMiAzLjMgMjAuM3oiLz48L3N2Zz4="; |
|
toggleIcon.addEventListener("click", function () { |
|
const passwordInput = document.getElementById("staticrypt-password"); |
|
if (passwordInput.type === "password") { |
|
passwordInput.type = "text"; |
|
toggleIcon.src = imgSrcEyeOpened; |
|
toggleIcon.alt = templateToggleAltHide; |
|
toggleIcon.title = templateToggleAltHide; |
|
} else { |
|
passwordInput.type = "password"; |
|
toggleIcon.src = imgSrcEyeClosed; |
|
toggleIcon.alt = templateToggleAltShow; |
|
toggleIcon.title = templateToggleAltShow; |
|
} |
|
}); |
|
|
|
|
|
document.getElementById("staticrypt-form").addEventListener("submit", async function (e) { |
|
e.preventDefault(); |
|
|
|
const password = document.getElementById("staticrypt-password").value, |
|
isRememberChecked = document.getElementById("staticrypt-remember").checked; |
|
|
|
const { isSuccessful } = await staticrypt.handleDecryptionOfPage(password, isRememberChecked); |
|
|
|
if (!isSuccessful) { |
|
alert(templateError); |
|
} |
|
}); |
|
</script> |
|
</body> |
|
</html> |
|
|