kopia lustrzana https://github.com/robinmoisson/staticrypt
rename passphrase into password
rodzic
4696a24324
commit
5ef120be53
|
@ -15,7 +15,7 @@ require('dotenv').config();
|
|||
const cryptoEngine = require("../lib/cryptoEngine.js");
|
||||
const codec = require("../lib/codec.js");
|
||||
const { generateRandomSalt } = cryptoEngine;
|
||||
const { encodeWithHashedPassphrase } = codec.init(cryptoEngine);
|
||||
const { encodeWithHashedPassword } = codec.init(cryptoEngine);
|
||||
const { parseCommandLineArguments, buildStaticryptJS, isOptionSetByUser, genFile, getFileContent,
|
||||
getValidatedSalt,
|
||||
getValidatedPassword, getConfig, writeConfig
|
||||
|
@ -69,7 +69,7 @@ async function runStatiCrypt() {
|
|||
if (hasShareFlag) {
|
||||
const url = namedArgs.share || "";
|
||||
|
||||
const hashedPassword = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
console.log(url + "#staticrypt_pwd=" + hashedPassword);
|
||||
process.exit(0);
|
||||
|
@ -96,7 +96,7 @@ async function runStatiCrypt() {
|
|||
template_color_secondary: namedArgs.templateColorSecondary,
|
||||
};
|
||||
|
||||
const hashedPassword = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
for (const positionalArgument of positionalArguments) {
|
||||
const inputFilepath = positionalArgument.toString();
|
||||
|
@ -105,7 +105,7 @@ async function runStatiCrypt() {
|
|||
const contents = getFileContent(inputFilepath);
|
||||
|
||||
// encrypt input
|
||||
const encryptedMsg = await encodeWithHashedPassphrase(contents, hashedPassword);
|
||||
const encryptedMsg = await encodeWithHashedPassword(contents, hashedPassword);
|
||||
|
||||
const staticryptConfig = {
|
||||
encryptedMsg,
|
||||
|
|
|
@ -251,14 +251,14 @@ const UTF8Encoder = {
|
|||
/**
|
||||
* Salt and encrypt a msg with a password.
|
||||
*/
|
||||
async function encrypt(msg, hashedPassphrase) {
|
||||
async function encrypt(msg, hashedPassword) {
|
||||
// Must be 16 bytes, unpredictable, and preferably cryptographically random. However, it need not be secret.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters
|
||||
const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8));
|
||||
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["encrypt"]
|
||||
|
@ -282,17 +282,17 @@ exports.encrypt = encrypt;
|
|||
* Decrypt a salted msg using a password.
|
||||
*
|
||||
* @param {string} encryptedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function decrypt(encryptedMsg, hashedPassphrase) {
|
||||
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(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["decrypt"]
|
||||
|
@ -312,33 +312,33 @@ async function decrypt(encryptedMsg, hashedPassphrase) {
|
|||
exports.decrypt = decrypt;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function hashPassphrase(passphrase, salt) {
|
||||
// we hash the passphrase in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
async function hashPassword(password, salt) {
|
||||
// we hash the password in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
// iterations, so for backward compatibility reasons, we need to support going from that to more iterations.
|
||||
let hashedPassphrase = await hashLegacyRound(passphrase, salt);
|
||||
let hashedPassword = await hashLegacyRound(password, salt);
|
||||
|
||||
hashedPassphrase = await hashSecondRound(hashedPassphrase, salt);
|
||||
hashedPassword = await hashSecondRound(hashedPassword, salt);
|
||||
|
||||
return hashThirdRound(hashedPassphrase, salt);
|
||||
return hashThirdRound(hashedPassword, salt);
|
||||
}
|
||||
exports.hashPassphrase = hashPassphrase;
|
||||
exports.hashPassword = hashPassword;
|
||||
|
||||
/**
|
||||
* This hashes the passphrase with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* This hashes the password with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* compatibility.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashLegacyRound(passphrase, salt) {
|
||||
return pbkdf2(passphrase, salt, 1000, "SHA-1");
|
||||
function hashLegacyRound(password, salt) {
|
||||
return pbkdf2(password, salt, 1000, "SHA-1");
|
||||
}
|
||||
exports.hashLegacyRound = hashLegacyRound;
|
||||
|
||||
|
@ -346,12 +346,12 @@ exports.hashLegacyRound = hashLegacyRound;
|
|||
* Add a second round of iterations. This is because we used to use 1k, so for backwards compatibility with
|
||||
* remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashSecondRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 14000, "SHA-256");
|
||||
function hashSecondRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 14000, "SHA-256");
|
||||
}
|
||||
exports.hashSecondRound = hashSecondRound;
|
||||
|
||||
|
@ -359,28 +359,28 @@ exports.hashSecondRound = hashSecondRound;
|
|||
* Add a third round of iterations to bring total number to 600k. This is because we used to use 1k, then 15k, so for
|
||||
* backwards compatibility with remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashThirdRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 585000, "SHA-256");
|
||||
function hashThirdRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 585000, "SHA-256");
|
||||
}
|
||||
exports.hashThirdRound = hashThirdRound;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @param {int} iterations
|
||||
* @param {string} hashAlgorithm
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function pbkdf2(passphrase, salt, iterations, hashAlgorithm) {
|
||||
async function pbkdf2(password, salt, iterations, hashAlgorithm) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
UTF8Encoder.parse(passphrase),
|
||||
UTF8Encoder.parse(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits"]
|
||||
|
@ -407,10 +407,10 @@ function generateRandomSalt() {
|
|||
}
|
||||
exports.generateRandomSalt = generateRandomSalt;
|
||||
|
||||
async function signMessage(hashedPassphrase, message) {
|
||||
async function signMessage(hashedPassword, message) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
{
|
||||
name: "HMAC",
|
||||
hash: "SHA-256",
|
||||
|
@ -486,13 +486,13 @@ function init(cryptoEngine) {
|
|||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encode(msg, password, salt) {
|
||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
|
@ -503,58 +503,58 @@ function init(cryptoEngine) {
|
|||
* we don't need to hash the password multiple times.
|
||||
*
|
||||
* @param {string} msg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
*
|
||||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
async function encodeWithHashedPassword(msg, hashedPassword) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||
exports.encodeWithHashedPassword = encodeWithHashedPassword;
|
||||
|
||||
/**
|
||||
* Top-level function for decoding a message.
|
||||
* Includes signature check and decryption.
|
||||
*
|
||||
* @param {string} signedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @param {string} salt
|
||||
* @param {int} backwardCompatibleAttempt
|
||||
* @param {string} originalPassphrase
|
||||
* @param {string} originalPassword
|
||||
*
|
||||
* @returns {Object} {success: true, decoded: string} | {success: false, message: string}
|
||||
*/
|
||||
async function decode(
|
||||
signedMsg,
|
||||
hashedPassphrase,
|
||||
hashedPassword,
|
||||
salt,
|
||||
backwardCompatibleAttempt = 0,
|
||||
originalPassphrase = ''
|
||||
originalPassword = ''
|
||||
) {
|
||||
const encryptedHMAC = signedMsg.substring(0, 64);
|
||||
const encryptedMsg = signedMsg.substring(64);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassphrase, encryptedMsg);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg);
|
||||
|
||||
if (decryptedHMAC !== encryptedHMAC) {
|
||||
// we have been raising the number of iterations in the hashing algorithm multiple times, so to support the old
|
||||
// remember-me/autodecrypt links we need to try bringing the old hashes up to speed.
|
||||
originalPassphrase = originalPassphrase || hashedPassphrase;
|
||||
originalPassword = originalPassword || hashedPassword;
|
||||
if (backwardCompatibleAttempt === 0) {
|
||||
const updatedHashedPassphrase = await cryptoEngine.hashThirdRound(originalPassphrase, salt);
|
||||
const updatedHashedPassword = await cryptoEngine.hashThirdRound(originalPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
if (backwardCompatibleAttempt === 1) {
|
||||
let updatedHashedPassphrase = await cryptoEngine.hashSecondRound(originalPassphrase, salt);
|
||||
updatedHashedPassphrase = await cryptoEngine.hashThirdRound(updatedHashedPassphrase, salt);
|
||||
let updatedHashedPassword = await cryptoEngine.hashSecondRound(originalPassword, salt);
|
||||
updatedHashedPassword = await cryptoEngine.hashThirdRound(updatedHashedPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
|
||||
return { success: false, message: "Signature mismatch" };
|
||||
|
@ -562,7 +562,7 @@ function init(cryptoEngine) {
|
|||
|
||||
return {
|
||||
success: true,
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassphrase),
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassword),
|
||||
};
|
||||
}
|
||||
exports.decode = decode;
|
||||
|
@ -599,14 +599,14 @@ function init(staticryptConfig, templateConfig) {
|
|||
/**
|
||||
* Decrypt our encrypted page, replace the whole HTML.
|
||||
*
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function decryptAndReplaceHtml(hashedPassphrase) {
|
||||
async function decryptAndReplaceHtml(hashedPassword) {
|
||||
const { encryptedMsg, salt } = staticryptConfig;
|
||||
const { replaceHtmlCallback } = templateConfig;
|
||||
|
||||
const result = await decode(encryptedMsg, hashedPassphrase, salt);
|
||||
const result = await decode(encryptedMsg, hashedPassword, salt);
|
||||
if (!result.success) {
|
||||
return false;
|
||||
}
|
||||
|
@ -637,7 +637,7 @@ function init(staticryptConfig, templateConfig) {
|
|||
const { rememberExpirationKey, rememberPassphraseKey } = templateConfig;
|
||||
|
||||
// decrypt and replace the whole page
|
||||
const hashedPassword = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword);
|
||||
|
||||
|
@ -744,11 +744,11 @@ function init(staticryptConfig, templateConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
const hashedPassphrase = localStorage.getItem(rememberPassphraseKey);
|
||||
const hashedPassword = localStorage.getItem(rememberPassphraseKey);
|
||||
|
||||
if (hashedPassphrase) {
|
||||
if (hashedPassword) {
|
||||
// try to decrypt
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassphrase);
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword);
|
||||
|
||||
// if the decryption is unsuccessful the password might be wrong - silently clear the saved data and let
|
||||
// the user fill the password form again
|
||||
|
@ -768,16 +768,16 @@ function init(staticryptConfig, templateConfig) {
|
|||
|
||||
// get the password from the query param
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const hashedPassphraseQuery = queryParams.get(passwordKey);
|
||||
const hashedPasswordQuery = queryParams.get(passwordKey);
|
||||
|
||||
// get the password from the url fragment
|
||||
const hashRegexMatch = window.location.hash.substring(1).match(new RegExp(passwordKey + "=(.*)"));
|
||||
const hashedPassphraseFragment = hashRegexMatch ? hashRegexMatch[1] : null;
|
||||
const hashedPasswordFragment = hashRegexMatch ? hashRegexMatch[1] : null;
|
||||
|
||||
const hashedPassphrase = hashedPassphraseFragment || hashedPassphraseQuery;
|
||||
const hashedPassword = hashedPasswordFragment || hashedPasswordQuery;
|
||||
|
||||
if (hashedPassphrase) {
|
||||
return decryptAndReplaceHtml(hashedPassphrase);
|
||||
if (hashedPassword) {
|
||||
return decryptAndReplaceHtml(hashedPassword);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -790,7 +790,7 @@ exports.init = init;
|
|||
})())
|
||||
const templateError = 'Bad password!',
|
||||
isRememberEnabled = true,
|
||||
staticryptConfig = {"encryptedMsg":"8ad22b42e255e1467bbd5369c84ae7420583384946876422f9450a5ab7fb3eaf33f5b73b1980988ef451c513871bb24582fabe01c8a5d0bde2a0580d564bf9c232cffc7fca5169005c024de005a25f479c172a65f6554405874082906b4a3be1ca7c0118728b25cd3ae915277e19f574c4f794affeffb2a1c3e923ea12fd83f5005a1c1b9606d36448082bd0f8ef70f8cc99f5205075e702b2f2b795c9150e20f2de2c6c86631c2d59ebbe2543c8ec13e450b21bbefdc2f2cb219190a0510538","isRememberEnabled":true,"rememberDurationInDays":0,"salt":"b93bbaf35459951c47721d1f3eaeb5b9"};
|
||||
staticryptConfig = {"encryptedMsg":"eaad887f76cba7e862577a8d69c79acfa062d4a1af097faf1fa2d5c56ca2383ea696b3a2f62c6507bebe2f6497d84bcbf7f989136c9ea7f6a95191c1da90a92c330615d9643f382beb9bc3eecaef67a1e2243913e3bd7165820eef4518ac95287e12c1af22bcdaf6190167da3215800ca6809cf5b011847b4618f36e53b40b39d77d174b52a5c8ffaf47f9309478f0d3739f29d606ebe5a2a2334bf6c0f06ed53cdf7f4d703a4a97a41e47b34c50ac4103b7c4a8f0ba92f31010fe097f57bf1c","isRememberEnabled":true,"rememberDurationInDays":0,"salt":"b93bbaf35459951c47721d1f3eaeb5b9"};
|
||||
|
||||
// you can edit these values to customize some of the behavior of StatiCrypt
|
||||
const templateConfig = {
|
||||
|
@ -826,10 +826,10 @@ exports.init = init;
|
|||
document.getElementById('staticrypt-form').addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const passphrase = document.getElementById('staticrypt-password').value,
|
||||
const password = document.getElementById('staticrypt-password').value,
|
||||
isRememberChecked = document.getElementById('staticrypt-remember').checked;
|
||||
|
||||
const { isSuccessful } = await staticrypt.handleDecryptionOfPage(passphrase, isRememberChecked);
|
||||
const { isSuccessful } = await staticrypt.handleDecryptionOfPage(password, isRememberChecked);
|
||||
|
||||
if (!isSuccessful) {
|
||||
alert(templateError);
|
||||
|
|
238
index.html
238
index.html
|
@ -96,8 +96,8 @@
|
|||
<div class="col-xs-12">
|
||||
<form id="encrypt_form">
|
||||
<div class="form-group">
|
||||
<label for="passphrase">Password</label>
|
||||
<input type="password" class="form-control" id="passphrase"
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" id="password"
|
||||
placeholder="Password (choose a long one!)">
|
||||
</div>
|
||||
|
||||
|
@ -137,9 +137,9 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="template_placeholder">Passphrase input placeholder</label>
|
||||
<label for="template_placeholder">Password input placeholder</label>
|
||||
<input type="text" class="form-control" id="template_placeholder"
|
||||
placeholder="Default: 'Passphrase'">
|
||||
placeholder="Default: 'Password'">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -155,7 +155,7 @@
|
|||
step="any"
|
||||
placeholder="Default: 0 (no expiration)">
|
||||
<small class="form-text text-muted">
|
||||
After this many days, the user will have to enter the passphrase again. Leave empty or set
|
||||
After this many days, the user will have to enter the password again. Leave empty or set
|
||||
to 0 for no expiration.
|
||||
</small>
|
||||
</div>
|
||||
|
@ -166,7 +166,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary pull-right" type="submit">Generate passphrase protected HTML</button>
|
||||
<button class="btn btn-primary pull-right" type="submit">Generate password protected HTML</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -254,14 +254,14 @@ const UTF8Encoder = {
|
|||
/**
|
||||
* Salt and encrypt a msg with a password.
|
||||
*/
|
||||
async function encrypt(msg, hashedPassphrase) {
|
||||
async function encrypt(msg, hashedPassword) {
|
||||
// Must be 16 bytes, unpredictable, and preferably cryptographically random. However, it need not be secret.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters
|
||||
const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8));
|
||||
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["encrypt"]
|
||||
|
@ -285,17 +285,17 @@ exports.encrypt = encrypt;
|
|||
* Decrypt a salted msg using a password.
|
||||
*
|
||||
* @param {string} encryptedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function decrypt(encryptedMsg, hashedPassphrase) {
|
||||
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(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["decrypt"]
|
||||
|
@ -315,33 +315,33 @@ async function decrypt(encryptedMsg, hashedPassphrase) {
|
|||
exports.decrypt = decrypt;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function hashPassphrase(passphrase, salt) {
|
||||
// we hash the passphrase in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
async function hashPassword(password, salt) {
|
||||
// we hash the password in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
// iterations, so for backward compatibility reasons, we need to support going from that to more iterations.
|
||||
let hashedPassphrase = await hashLegacyRound(passphrase, salt);
|
||||
let hashedPassword = await hashLegacyRound(password, salt);
|
||||
|
||||
hashedPassphrase = await hashSecondRound(hashedPassphrase, salt);
|
||||
hashedPassword = await hashSecondRound(hashedPassword, salt);
|
||||
|
||||
return hashThirdRound(hashedPassphrase, salt);
|
||||
return hashThirdRound(hashedPassword, salt);
|
||||
}
|
||||
exports.hashPassphrase = hashPassphrase;
|
||||
exports.hashPassword = hashPassword;
|
||||
|
||||
/**
|
||||
* This hashes the passphrase with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* This hashes the password with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* compatibility.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashLegacyRound(passphrase, salt) {
|
||||
return pbkdf2(passphrase, salt, 1000, "SHA-1");
|
||||
function hashLegacyRound(password, salt) {
|
||||
return pbkdf2(password, salt, 1000, "SHA-1");
|
||||
}
|
||||
exports.hashLegacyRound = hashLegacyRound;
|
||||
|
||||
|
@ -349,12 +349,12 @@ exports.hashLegacyRound = hashLegacyRound;
|
|||
* Add a second round of iterations. This is because we used to use 1k, so for backwards compatibility with
|
||||
* remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashSecondRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 14000, "SHA-256");
|
||||
function hashSecondRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 14000, "SHA-256");
|
||||
}
|
||||
exports.hashSecondRound = hashSecondRound;
|
||||
|
||||
|
@ -362,28 +362,28 @@ exports.hashSecondRound = hashSecondRound;
|
|||
* Add a third round of iterations to bring total number to 600k. This is because we used to use 1k, then 15k, so for
|
||||
* backwards compatibility with remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashThirdRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 585000, "SHA-256");
|
||||
function hashThirdRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 585000, "SHA-256");
|
||||
}
|
||||
exports.hashThirdRound = hashThirdRound;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @param {int} iterations
|
||||
* @param {string} hashAlgorithm
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function pbkdf2(passphrase, salt, iterations, hashAlgorithm) {
|
||||
async function pbkdf2(password, salt, iterations, hashAlgorithm) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
UTF8Encoder.parse(passphrase),
|
||||
UTF8Encoder.parse(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits"]
|
||||
|
@ -410,10 +410,10 @@ function generateRandomSalt() {
|
|||
}
|
||||
exports.generateRandomSalt = generateRandomSalt;
|
||||
|
||||
async function signMessage(hashedPassphrase, message) {
|
||||
async function signMessage(hashedPassword, message) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
{
|
||||
name: "HMAC",
|
||||
hash: "SHA-256",
|
||||
|
@ -492,13 +492,13 @@ function init(cryptoEngine) {
|
|||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encode(msg, password, salt) {
|
||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
|
@ -509,58 +509,58 @@ function init(cryptoEngine) {
|
|||
* we don't need to hash the password multiple times.
|
||||
*
|
||||
* @param {string} msg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
*
|
||||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
async function encodeWithHashedPassword(msg, hashedPassword) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||
exports.encodeWithHashedPassword = encodeWithHashedPassword;
|
||||
|
||||
/**
|
||||
* Top-level function for decoding a message.
|
||||
* Includes signature check and decryption.
|
||||
*
|
||||
* @param {string} signedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @param {string} salt
|
||||
* @param {int} backwardCompatibleAttempt
|
||||
* @param {string} originalPassphrase
|
||||
* @param {string} originalPassword
|
||||
*
|
||||
* @returns {Object} {success: true, decoded: string} | {success: false, message: string}
|
||||
*/
|
||||
async function decode(
|
||||
signedMsg,
|
||||
hashedPassphrase,
|
||||
hashedPassword,
|
||||
salt,
|
||||
backwardCompatibleAttempt = 0,
|
||||
originalPassphrase = ''
|
||||
originalPassword = ''
|
||||
) {
|
||||
const encryptedHMAC = signedMsg.substring(0, 64);
|
||||
const encryptedMsg = signedMsg.substring(64);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassphrase, encryptedMsg);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg);
|
||||
|
||||
if (decryptedHMAC !== encryptedHMAC) {
|
||||
// we have been raising the number of iterations in the hashing algorithm multiple times, so to support the old
|
||||
// remember-me/autodecrypt links we need to try bringing the old hashes up to speed.
|
||||
originalPassphrase = originalPassphrase || hashedPassphrase;
|
||||
originalPassword = originalPassword || hashedPassword;
|
||||
if (backwardCompatibleAttempt === 0) {
|
||||
const updatedHashedPassphrase = await cryptoEngine.hashThirdRound(originalPassphrase, salt);
|
||||
const updatedHashedPassword = await cryptoEngine.hashThirdRound(originalPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
if (backwardCompatibleAttempt === 1) {
|
||||
let updatedHashedPassphrase = await cryptoEngine.hashSecondRound(originalPassphrase, salt);
|
||||
updatedHashedPassphrase = await cryptoEngine.hashThirdRound(updatedHashedPassphrase, salt);
|
||||
let updatedHashedPassword = await cryptoEngine.hashSecondRound(originalPassword, salt);
|
||||
updatedHashedPassword = await cryptoEngine.hashThirdRound(updatedHashedPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
|
||||
return { success: false, message: "Signature mismatch" };
|
||||
|
@ -568,7 +568,7 @@ function init(cryptoEngine) {
|
|||
|
||||
return {
|
||||
success: true,
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassphrase),
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassword),
|
||||
};
|
||||
}
|
||||
exports.decode = decode;
|
||||
|
@ -686,14 +686,14 @@ const UTF8Encoder = {
|
|||
/**
|
||||
* Salt and encrypt a msg with a password.
|
||||
*/
|
||||
async function encrypt(msg, hashedPassphrase) {
|
||||
async function encrypt(msg, hashedPassword) {
|
||||
// Must be 16 bytes, unpredictable, and preferably cryptographically random. However, it need not be secret.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters
|
||||
const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8));
|
||||
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["encrypt"]
|
||||
|
@ -717,17 +717,17 @@ exports.encrypt = encrypt;
|
|||
* Decrypt a salted msg using a password.
|
||||
*
|
||||
* @param {string} encryptedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function decrypt(encryptedMsg, hashedPassphrase) {
|
||||
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(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["decrypt"]
|
||||
|
@ -747,33 +747,33 @@ async function decrypt(encryptedMsg, hashedPassphrase) {
|
|||
exports.decrypt = decrypt;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function hashPassphrase(passphrase, salt) {
|
||||
// we hash the passphrase in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
async function hashPassword(password, salt) {
|
||||
// we hash the password in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
// iterations, so for backward compatibility reasons, we need to support going from that to more iterations.
|
||||
let hashedPassphrase = await hashLegacyRound(passphrase, salt);
|
||||
let hashedPassword = await hashLegacyRound(password, salt);
|
||||
|
||||
hashedPassphrase = await hashSecondRound(hashedPassphrase, salt);
|
||||
hashedPassword = await hashSecondRound(hashedPassword, salt);
|
||||
|
||||
return hashThirdRound(hashedPassphrase, salt);
|
||||
return hashThirdRound(hashedPassword, salt);
|
||||
}
|
||||
exports.hashPassphrase = hashPassphrase;
|
||||
exports.hashPassword = hashPassword;
|
||||
|
||||
/**
|
||||
* This hashes the passphrase with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* This hashes the password with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* compatibility.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashLegacyRound(passphrase, salt) {
|
||||
return pbkdf2(passphrase, salt, 1000, "SHA-1");
|
||||
function hashLegacyRound(password, salt) {
|
||||
return pbkdf2(password, salt, 1000, "SHA-1");
|
||||
}
|
||||
exports.hashLegacyRound = hashLegacyRound;
|
||||
|
||||
|
@ -781,12 +781,12 @@ exports.hashLegacyRound = hashLegacyRound;
|
|||
* Add a second round of iterations. This is because we used to use 1k, so for backwards compatibility with
|
||||
* remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashSecondRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 14000, "SHA-256");
|
||||
function hashSecondRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 14000, "SHA-256");
|
||||
}
|
||||
exports.hashSecondRound = hashSecondRound;
|
||||
|
||||
|
@ -794,28 +794,28 @@ exports.hashSecondRound = hashSecondRound;
|
|||
* Add a third round of iterations to bring total number to 600k. This is because we used to use 1k, then 15k, so for
|
||||
* backwards compatibility with remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashThirdRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 585000, "SHA-256");
|
||||
function hashThirdRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 585000, "SHA-256");
|
||||
}
|
||||
exports.hashThirdRound = hashThirdRound;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @param {int} iterations
|
||||
* @param {string} hashAlgorithm
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function pbkdf2(passphrase, salt, iterations, hashAlgorithm) {
|
||||
async function pbkdf2(password, salt, iterations, hashAlgorithm) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
UTF8Encoder.parse(passphrase),
|
||||
UTF8Encoder.parse(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits"]
|
||||
|
@ -842,10 +842,10 @@ function generateRandomSalt() {
|
|||
}
|
||||
exports.generateRandomSalt = generateRandomSalt;
|
||||
|
||||
async function signMessage(hashedPassphrase, message) {
|
||||
async function signMessage(hashedPassword, message) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
{
|
||||
name: "HMAC",
|
||||
hash: "SHA-256",
|
||||
|
@ -921,13 +921,13 @@ function init(cryptoEngine) {
|
|||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encode(msg, password, salt) {
|
||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
|
@ -938,58 +938,58 @@ function init(cryptoEngine) {
|
|||
* we don't need to hash the password multiple times.
|
||||
*
|
||||
* @param {string} msg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
*
|
||||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
async function encodeWithHashedPassword(msg, hashedPassword) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||
exports.encodeWithHashedPassword = encodeWithHashedPassword;
|
||||
|
||||
/**
|
||||
* Top-level function for decoding a message.
|
||||
* Includes signature check and decryption.
|
||||
*
|
||||
* @param {string} signedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @param {string} salt
|
||||
* @param {int} backwardCompatibleAttempt
|
||||
* @param {string} originalPassphrase
|
||||
* @param {string} originalPassword
|
||||
*
|
||||
* @returns {Object} {success: true, decoded: string} | {success: false, message: string}
|
||||
*/
|
||||
async function decode(
|
||||
signedMsg,
|
||||
hashedPassphrase,
|
||||
hashedPassword,
|
||||
salt,
|
||||
backwardCompatibleAttempt = 0,
|
||||
originalPassphrase = ''
|
||||
originalPassword = ''
|
||||
) {
|
||||
const encryptedHMAC = signedMsg.substring(0, 64);
|
||||
const encryptedMsg = signedMsg.substring(64);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassphrase, encryptedMsg);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg);
|
||||
|
||||
if (decryptedHMAC !== encryptedHMAC) {
|
||||
// we have been raising the number of iterations in the hashing algorithm multiple times, so to support the old
|
||||
// remember-me/autodecrypt links we need to try bringing the old hashes up to speed.
|
||||
originalPassphrase = originalPassphrase || hashedPassphrase;
|
||||
originalPassword = originalPassword || hashedPassword;
|
||||
if (backwardCompatibleAttempt === 0) {
|
||||
const updatedHashedPassphrase = await cryptoEngine.hashThirdRound(originalPassphrase, salt);
|
||||
const updatedHashedPassword = await cryptoEngine.hashThirdRound(originalPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
if (backwardCompatibleAttempt === 1) {
|
||||
let updatedHashedPassphrase = await cryptoEngine.hashSecondRound(originalPassphrase, salt);
|
||||
updatedHashedPassphrase = await cryptoEngine.hashThirdRound(updatedHashedPassphrase, salt);
|
||||
let updatedHashedPassword = await cryptoEngine.hashSecondRound(originalPassword, salt);
|
||||
updatedHashedPassword = await cryptoEngine.hashThirdRound(updatedHashedPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
|
||||
return { success: false, message: "Signature mismatch" };
|
||||
|
@ -997,7 +997,7 @@ function init(cryptoEngine) {
|
|||
|
||||
return {
|
||||
success: true,
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassphrase),
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassword),
|
||||
};
|
||||
}
|
||||
exports.decode = decode;
|
||||
|
@ -1034,14 +1034,14 @@ function init(staticryptConfig, templateConfig) {
|
|||
/**
|
||||
* Decrypt our encrypted page, replace the whole HTML.
|
||||
*
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function decryptAndReplaceHtml(hashedPassphrase) {
|
||||
async function decryptAndReplaceHtml(hashedPassword) {
|
||||
const { encryptedMsg, salt } = staticryptConfig;
|
||||
const { replaceHtmlCallback } = templateConfig;
|
||||
|
||||
const result = await decode(encryptedMsg, hashedPassphrase, salt);
|
||||
const result = await decode(encryptedMsg, hashedPassword, salt);
|
||||
if (!result.success) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1072,7 +1072,7 @@ function init(staticryptConfig, templateConfig) {
|
|||
const { rememberExpirationKey, rememberPassphraseKey } = templateConfig;
|
||||
|
||||
// decrypt and replace the whole page
|
||||
const hashedPassword = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword);
|
||||
|
||||
|
@ -1179,11 +1179,11 @@ function init(staticryptConfig, templateConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
const hashedPassphrase = localStorage.getItem(rememberPassphraseKey);
|
||||
const hashedPassword = localStorage.getItem(rememberPassphraseKey);
|
||||
|
||||
if (hashedPassphrase) {
|
||||
if (hashedPassword) {
|
||||
// try to decrypt
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassphrase);
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword);
|
||||
|
||||
// if the decryption is unsuccessful the password might be wrong - silently clear the saved data and let
|
||||
// the user fill the password form again
|
||||
|
@ -1203,16 +1203,16 @@ function init(staticryptConfig, templateConfig) {
|
|||
|
||||
// get the password from the query param
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const hashedPassphraseQuery = queryParams.get(passwordKey);
|
||||
const hashedPasswordQuery = queryParams.get(passwordKey);
|
||||
|
||||
// get the password from the url fragment
|
||||
const hashRegexMatch = window.location.hash.substring(1).match(new RegExp(passwordKey + "=(.*)"));
|
||||
const hashedPassphraseFragment = hashRegexMatch ? hashRegexMatch[1] : null;
|
||||
const hashedPasswordFragment = hashRegexMatch ? hashRegexMatch[1] : null;
|
||||
|
||||
const hashedPassphrase = hashedPassphraseFragment || hashedPassphraseQuery;
|
||||
const hashedPassword = hashedPasswordFragment || hashedPasswordQuery;
|
||||
|
||||
if (hashedPassphrase) {
|
||||
return decryptAndReplaceHtml(hashedPassphrase);
|
||||
if (hashedPassword) {
|
||||
return decryptAndReplaceHtml(hashedPassword);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1300,10 +1300,10 @@ exports.init = init;
|
|||
CKEDITOR.instances['template_instructions'].updateElement();
|
||||
|
||||
const unencrypted = document.getElementById('unencrypted_html').value,
|
||||
passphrase = document.getElementById('passphrase').value;
|
||||
password = document.getElementById('password').value;
|
||||
|
||||
const salt = cryptoEngine.generateRandomSalt();
|
||||
const encryptedMsg = await encode(unencrypted, passphrase, salt);
|
||||
const encryptedMsg = await encode(unencrypted, password, salt);
|
||||
|
||||
const templateButton = document.getElementById('template_button').value,
|
||||
templateInstructions = document.getElementById('template_instructions').value,
|
||||
|
@ -1324,7 +1324,7 @@ exports.init = init;
|
|||
js_staticrypt: getScriptAsString('staticrypt'),
|
||||
template_button: templateButton ? templateButton : 'DECRYPT',
|
||||
template_instructions: templateInstructions || '',
|
||||
template_placeholder: templatePlaceholder || 'Passphrase',
|
||||
template_placeholder: templatePlaceholder || 'Password',
|
||||
template_remember: templateRemember || 'Remember me',
|
||||
template_title: templateTitle || 'Protected Page',
|
||||
};
|
||||
|
|
40
lib/codec.js
40
lib/codec.js
|
@ -17,13 +17,13 @@ function init(cryptoEngine) {
|
|||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encode(msg, password, salt) {
|
||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
|
@ -34,58 +34,58 @@ function init(cryptoEngine) {
|
|||
* we don't need to hash the password multiple times.
|
||||
*
|
||||
* @param {string} msg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
*
|
||||
* @returns {string} The encoded text
|
||||
*/
|
||||
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
async function encodeWithHashedPassword(msg, hashedPassword) {
|
||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassword);
|
||||
|
||||
// we use the hashed password in the HMAC because this is effectively what will be used a password (so we can store
|
||||
// it in localStorage safely, we don't use the clear text password)
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
const hmac = await cryptoEngine.signMessage(hashedPassword, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||
exports.encodeWithHashedPassword = encodeWithHashedPassword;
|
||||
|
||||
/**
|
||||
* Top-level function for decoding a message.
|
||||
* Includes signature check and decryption.
|
||||
*
|
||||
* @param {string} signedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @param {string} salt
|
||||
* @param {int} backwardCompatibleAttempt
|
||||
* @param {string} originalPassphrase
|
||||
* @param {string} originalPassword
|
||||
*
|
||||
* @returns {Object} {success: true, decoded: string} | {success: false, message: string}
|
||||
*/
|
||||
async function decode(
|
||||
signedMsg,
|
||||
hashedPassphrase,
|
||||
hashedPassword,
|
||||
salt,
|
||||
backwardCompatibleAttempt = 0,
|
||||
originalPassphrase = ''
|
||||
originalPassword = ''
|
||||
) {
|
||||
const encryptedHMAC = signedMsg.substring(0, 64);
|
||||
const encryptedMsg = signedMsg.substring(64);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassphrase, encryptedMsg);
|
||||
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg);
|
||||
|
||||
if (decryptedHMAC !== encryptedHMAC) {
|
||||
// we have been raising the number of iterations in the hashing algorithm multiple times, so to support the old
|
||||
// remember-me/autodecrypt links we need to try bringing the old hashes up to speed.
|
||||
originalPassphrase = originalPassphrase || hashedPassphrase;
|
||||
originalPassword = originalPassword || hashedPassword;
|
||||
if (backwardCompatibleAttempt === 0) {
|
||||
const updatedHashedPassphrase = await cryptoEngine.hashThirdRound(originalPassphrase, salt);
|
||||
const updatedHashedPassword = await cryptoEngine.hashThirdRound(originalPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
if (backwardCompatibleAttempt === 1) {
|
||||
let updatedHashedPassphrase = await cryptoEngine.hashSecondRound(originalPassphrase, salt);
|
||||
updatedHashedPassphrase = await cryptoEngine.hashThirdRound(updatedHashedPassphrase, salt);
|
||||
let updatedHashedPassword = await cryptoEngine.hashSecondRound(originalPassword, salt);
|
||||
updatedHashedPassword = await cryptoEngine.hashThirdRound(updatedHashedPassword, salt);
|
||||
|
||||
return decode(signedMsg, updatedHashedPassphrase, salt, backwardCompatibleAttempt + 1, originalPassphrase);
|
||||
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
||||
}
|
||||
|
||||
return { success: false, message: "Signature mismatch" };
|
||||
|
@ -93,7 +93,7 @@ function init(cryptoEngine) {
|
|||
|
||||
return {
|
||||
success: true,
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassphrase),
|
||||
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassword),
|
||||
};
|
||||
}
|
||||
exports.decode = decode;
|
||||
|
|
|
@ -64,14 +64,14 @@ const UTF8Encoder = {
|
|||
/**
|
||||
* Salt and encrypt a msg with a password.
|
||||
*/
|
||||
async function encrypt(msg, hashedPassphrase) {
|
||||
async function encrypt(msg, hashedPassword) {
|
||||
// Must be 16 bytes, unpredictable, and preferably cryptographically random. However, it need not be secret.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#parameters
|
||||
const iv = crypto.getRandomValues(new Uint8Array(IV_BITS / 8));
|
||||
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["encrypt"]
|
||||
|
@ -95,17 +95,17 @@ exports.encrypt = encrypt;
|
|||
* Decrypt a salted msg using a password.
|
||||
*
|
||||
* @param {string} encryptedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function decrypt(encryptedMsg, hashedPassphrase) {
|
||||
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(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
ENCRYPTION_ALGO,
|
||||
false,
|
||||
["decrypt"]
|
||||
|
@ -125,33 +125,33 @@ async function decrypt(encryptedMsg, hashedPassphrase) {
|
|||
exports.decrypt = decrypt;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function hashPassphrase(passphrase, salt) {
|
||||
// we hash the passphrase in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
async function hashPassword(password, salt) {
|
||||
// we hash the password in multiple steps, each adding more iterations. This is because we used to allow less
|
||||
// iterations, so for backward compatibility reasons, we need to support going from that to more iterations.
|
||||
let hashedPassphrase = await hashLegacyRound(passphrase, salt);
|
||||
let hashedPassword = await hashLegacyRound(password, salt);
|
||||
|
||||
hashedPassphrase = await hashSecondRound(hashedPassphrase, salt);
|
||||
hashedPassword = await hashSecondRound(hashedPassword, salt);
|
||||
|
||||
return hashThirdRound(hashedPassphrase, salt);
|
||||
return hashThirdRound(hashedPassword, salt);
|
||||
}
|
||||
exports.hashPassphrase = hashPassphrase;
|
||||
exports.hashPassword = hashPassword;
|
||||
|
||||
/**
|
||||
* This hashes the passphrase with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* This hashes the password with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* compatibility.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashLegacyRound(passphrase, salt) {
|
||||
return pbkdf2(passphrase, salt, 1000, "SHA-1");
|
||||
function hashLegacyRound(password, salt) {
|
||||
return pbkdf2(password, salt, 1000, "SHA-1");
|
||||
}
|
||||
exports.hashLegacyRound = hashLegacyRound;
|
||||
|
||||
|
@ -159,12 +159,12 @@ exports.hashLegacyRound = hashLegacyRound;
|
|||
* Add a second round of iterations. This is because we used to use 1k, so for backwards compatibility with
|
||||
* remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashSecondRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 14000, "SHA-256");
|
||||
function hashSecondRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 14000, "SHA-256");
|
||||
}
|
||||
exports.hashSecondRound = hashSecondRound;
|
||||
|
||||
|
@ -172,28 +172,28 @@ exports.hashSecondRound = hashSecondRound;
|
|||
* Add a third round of iterations to bring total number to 600k. This is because we used to use 1k, then 15k, so for
|
||||
* backwards compatibility with remember-me/autodecrypt links, we need to support going from that to more iterations.
|
||||
*
|
||||
* @param hashedPassphrase
|
||||
* @param hashedPassword
|
||||
* @param salt
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function hashThirdRound(hashedPassphrase, salt) {
|
||||
return pbkdf2(hashedPassphrase, salt, 585000, "SHA-256");
|
||||
function hashThirdRound(hashedPassword, salt) {
|
||||
return pbkdf2(hashedPassword, salt, 585000, "SHA-256");
|
||||
}
|
||||
exports.hashThirdRound = hashThirdRound;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
* Salt and hash the password so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @param {int} iterations
|
||||
* @param {string} hashAlgorithm
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function pbkdf2(passphrase, salt, iterations, hashAlgorithm) {
|
||||
async function pbkdf2(password, salt, iterations, hashAlgorithm) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
UTF8Encoder.parse(passphrase),
|
||||
UTF8Encoder.parse(password),
|
||||
"PBKDF2",
|
||||
false,
|
||||
["deriveBits"]
|
||||
|
@ -220,10 +220,10 @@ function generateRandomSalt() {
|
|||
}
|
||||
exports.generateRandomSalt = generateRandomSalt;
|
||||
|
||||
async function signMessage(hashedPassphrase, message) {
|
||||
async function signMessage(hashedPassword, message) {
|
||||
const key = await subtle.importKey(
|
||||
"raw",
|
||||
HexEncoder.parse(hashedPassphrase),
|
||||
HexEncoder.parse(hashedPassword),
|
||||
{
|
||||
name: "HMAC",
|
||||
hash: "SHA-256",
|
||||
|
|
|
@ -221,10 +221,10 @@
|
|||
document.getElementById('staticrypt-form').addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const passphrase = document.getElementById('staticrypt-password').value,
|
||||
const password = document.getElementById('staticrypt-password').value,
|
||||
isRememberChecked = document.getElementById('staticrypt-remember').checked;
|
||||
|
||||
const { isSuccessful } = await staticrypt.handleDecryptionOfPage(passphrase, isRememberChecked);
|
||||
const { isSuccessful } = await staticrypt.handleDecryptionOfPage(password, isRememberChecked);
|
||||
|
||||
if (!isSuccessful) {
|
||||
alert(templateError);
|
||||
|
|
|
@ -26,14 +26,14 @@ function init(staticryptConfig, templateConfig) {
|
|||
/**
|
||||
* Decrypt our encrypted page, replace the whole HTML.
|
||||
*
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} hashedPassword
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function decryptAndReplaceHtml(hashedPassphrase) {
|
||||
async function decryptAndReplaceHtml(hashedPassword) {
|
||||
const { encryptedMsg, salt } = staticryptConfig;
|
||||
const { replaceHtmlCallback } = templateConfig;
|
||||
|
||||
const result = await decode(encryptedMsg, hashedPassphrase, salt);
|
||||
const result = await decode(encryptedMsg, hashedPassword, salt);
|
||||
if (!result.success) {
|
||||
return false;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ function init(staticryptConfig, templateConfig) {
|
|||
const { rememberExpirationKey, rememberPassphraseKey } = templateConfig;
|
||||
|
||||
// decrypt and replace the whole page
|
||||
const hashedPassword = await cryptoEngine.hashPassphrase(password, salt);
|
||||
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
||||
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword);
|
||||
|
||||
|
@ -171,11 +171,11 @@ function init(staticryptConfig, templateConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
const hashedPassphrase = localStorage.getItem(rememberPassphraseKey);
|
||||
const hashedPassword = localStorage.getItem(rememberPassphraseKey);
|
||||
|
||||
if (hashedPassphrase) {
|
||||
if (hashedPassword) {
|
||||
// try to decrypt
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassphrase);
|
||||
const isDecryptionSuccessful = await decryptAndReplaceHtml(hashedPassword);
|
||||
|
||||
// if the decryption is unsuccessful the password might be wrong - silently clear the saved data and let
|
||||
// the user fill the password form again
|
||||
|
@ -195,16 +195,16 @@ function init(staticryptConfig, templateConfig) {
|
|||
|
||||
// get the password from the query param
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const hashedPassphraseQuery = queryParams.get(passwordKey);
|
||||
const hashedPasswordQuery = queryParams.get(passwordKey);
|
||||
|
||||
// get the password from the url fragment
|
||||
const hashRegexMatch = window.location.hash.substring(1).match(new RegExp(passwordKey + "=(.*)"));
|
||||
const hashedPassphraseFragment = hashRegexMatch ? hashRegexMatch[1] : null;
|
||||
const hashedPasswordFragment = hashRegexMatch ? hashRegexMatch[1] : null;
|
||||
|
||||
const hashedPassphrase = hashedPassphraseFragment || hashedPassphraseQuery;
|
||||
const hashedPassword = hashedPasswordFragment || hashedPasswordQuery;
|
||||
|
||||
if (hashedPassphrase) {
|
||||
return decryptAndReplaceHtml(hashedPassphrase);
|
||||
if (hashedPassword) {
|
||||
return decryptAndReplaceHtml(hashedPassword);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "staticrypt",
|
||||
"version": "3.2.0",
|
||||
"description": "Based on the [crypto-js](https://github.com/brix/crypto-js) library, StatiCrypt uses AES-256 to encrypt your input with your passphrase and put it in a HTML file with a password prompt that can decrypted in-browser (client side).",
|
||||
"description": "Based on the [crypto-js](https://github.com/brix/crypto-js) library, StatiCrypt uses AES-256 to encrypt your input with your long password and put it in a HTML file with a password prompt that can decrypted in-browser (client side).",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"/cli",
|
||||
|
|
|
@ -96,8 +96,8 @@
|
|||
<div class="col-xs-12">
|
||||
<form id="encrypt_form">
|
||||
<div class="form-group">
|
||||
<label for="passphrase">Password</label>
|
||||
<input type="password" class="form-control" id="passphrase"
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" id="password"
|
||||
placeholder="Password (choose a long one!)">
|
||||
</div>
|
||||
|
||||
|
@ -137,9 +137,9 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="template_placeholder">Passphrase input placeholder</label>
|
||||
<label for="template_placeholder">Password input placeholder</label>
|
||||
<input type="text" class="form-control" id="template_placeholder"
|
||||
placeholder="Default: 'Passphrase'">
|
||||
placeholder="Default: 'Password'">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -155,7 +155,7 @@
|
|||
step="any"
|
||||
placeholder="Default: 0 (no expiration)">
|
||||
<small class="form-text text-muted">
|
||||
After this many days, the user will have to enter the passphrase again. Leave empty or set
|
||||
After this many days, the user will have to enter the password again. Leave empty or set
|
||||
to 0 for no expiration.
|
||||
</small>
|
||||
</div>
|
||||
|
@ -166,7 +166,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary pull-right" type="submit">Generate passphrase protected HTML</button>
|
||||
<button class="btn btn-primary pull-right" type="submit">Generate password protected HTML</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -277,10 +277,10 @@ Your encrypted string</pre>
|
|||
CKEDITOR.instances['template_instructions'].updateElement();
|
||||
|
||||
const unencrypted = document.getElementById('unencrypted_html').value,
|
||||
passphrase = document.getElementById('passphrase').value;
|
||||
password = document.getElementById('password').value;
|
||||
|
||||
const salt = cryptoEngine.generateRandomSalt();
|
||||
const encryptedMsg = await encode(unencrypted, passphrase, salt);
|
||||
const encryptedMsg = await encode(unencrypted, password, salt);
|
||||
|
||||
const templateButton = document.getElementById('template_button').value,
|
||||
templateInstructions = document.getElementById('template_instructions').value,
|
||||
|
@ -301,7 +301,7 @@ Your encrypted string</pre>
|
|||
js_staticrypt: getScriptAsString('staticrypt'),
|
||||
template_button: templateButton ? templateButton : 'DECRYPT',
|
||||
template_instructions: templateInstructions || '',
|
||||
template_placeholder: templatePlaceholder || 'Passphrase',
|
||||
template_placeholder: templatePlaceholder || 'Password',
|
||||
template_remember: templateRemember || 'Remember me',
|
||||
template_title: templateTitle || 'Protected Page',
|
||||
};
|
||||
|
|
Ładowanie…
Reference in New Issue