kopia lustrzana https://github.com/robinmoisson/staticrypt
make WebCrypto the only engine available (closes #168)
rodzic
7021e3ad8d
commit
92691a1994
|
@ -1,6 +1,6 @@
|
|||
const fs = require("fs");
|
||||
|
||||
const { generateRandomSalt } = require("../lib/cryptoEngine/webcryptoEngine.js");
|
||||
const { generateRandomSalt } = require("../lib/cryptoEngine.js");
|
||||
const path = require("path");
|
||||
const {renderTemplate} = require("../lib/formater.js");
|
||||
const Yargs = require("yargs");
|
||||
|
@ -168,39 +168,6 @@ function genFile(data, outputFilePath, templateFilePath) {
|
|||
}
|
||||
exports.genFile = genFile;
|
||||
|
||||
/**
|
||||
* TODO: remove in next major version
|
||||
*
|
||||
* This method checks whether the password template support the security fix increasing PBKDF2 iterations. Users using
|
||||
* an old custom password_template might have logic that doesn't benefit from the fix.
|
||||
*
|
||||
* @param {string} templatePathParameter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isCustomPasswordTemplateLegacy(templatePathParameter) {
|
||||
const customTemplateContent = readFile(templatePathParameter, "template");
|
||||
|
||||
// if the template injects the crypto engine, it's up to date
|
||||
return !customTemplateContent.includes("js_crypto_engine");
|
||||
}
|
||||
exports.isCustomPasswordTemplateLegacy = isCustomPasswordTemplateLegacy;
|
||||
|
||||
/**
|
||||
* TODO: remove in next major version
|
||||
*
|
||||
* This method checks whether the password template support the async logic.
|
||||
*
|
||||
* @param {string} templatePathParameter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isPasswordTemplateUsingAsync(templatePathParameter) {
|
||||
const customTemplateContent = readFile(templatePathParameter, "template");
|
||||
|
||||
// if the template includes this comment, it's up to date
|
||||
return customTemplateContent.includes("// STATICRYPT_VERSION: async");
|
||||
}
|
||||
exports.isPasswordTemplateUsingAsync = isPasswordTemplateUsingAsync;
|
||||
|
||||
/**
|
||||
* @param {string} templatePathParameter
|
||||
* @returns {boolean}
|
||||
|
@ -224,18 +191,6 @@ function parseCommandLineArguments() {
|
|||
describe: 'Label to use for the decrypt button. Default: "DECRYPT".',
|
||||
default: "DECRYPT",
|
||||
})
|
||||
.option("e", {
|
||||
alias: "embed",
|
||||
type: "boolean",
|
||||
describe: "Whether or not to embed crypto-js in the page (or use an external CDN).",
|
||||
default: true,
|
||||
})
|
||||
.option("engine", {
|
||||
type: "string",
|
||||
describe: "The crypto engine to use. WebCrypto uses 600k iterations and is more secure, CryptoJS 15k.\n" +
|
||||
"Possible values: 'cryptojs', 'webcrypto'.",
|
||||
default: "cryptojs",
|
||||
})
|
||||
.option("f", {
|
||||
alias: "file-template",
|
||||
type: "string",
|
||||
|
|
118
cli/index.js
118
cli/index.js
|
@ -3,31 +3,21 @@
|
|||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// parse .env file into process.env
|
||||
require('dotenv').config();
|
||||
|
||||
const cryptojsEngine = require("../lib/cryptoEngine/cryptojsEngine");
|
||||
const webcryptoEngine = require("../lib/cryptoEngine/webcryptoEngine");
|
||||
const cryptoEngine = require("../lib/cryptoEngine.js");
|
||||
const codec = require("../lib/codec.js");
|
||||
const { generateRandomSalt, generateRandomString } = cryptoEngine;
|
||||
const { encode } = codec.init(cryptoEngine);
|
||||
const { convertCommonJSToBrowserJS, exitWithError, isOptionSetByUser, genFile, getPassword, getFileContent, getSalt} = require("./helpers");
|
||||
const { isCustomPasswordTemplateLegacy, parseCommandLineArguments, isPasswordTemplateUsingAsync} = require("./helpers.js");
|
||||
|
||||
const CRYPTOJS_SCRIPT_TAG =
|
||||
'<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js" ' +
|
||||
'integrity="sha384-lp4k1VRKPU9eBnPePjnJ9M2RF3i7PC30gXs70+elCVfgwLwx1tv5+ctxdtwxqZa7" crossorigin="anonymous"></script>';
|
||||
const { parseCommandLineArguments} = require("./helpers.js");
|
||||
|
||||
// parse arguments
|
||||
const yargs = parseCommandLineArguments();
|
||||
const namedArgs = yargs.argv;
|
||||
|
||||
// set the crypto engine
|
||||
const isWebcrypto = namedArgs.engine === "webcrypto";
|
||||
const cryptoEngine = isWebcrypto ? webcryptoEngine : cryptojsEngine;
|
||||
const { generateRandomSalt, generateRandomString } = cryptoEngine;
|
||||
const { encode } = codec.init(cryptoEngine);
|
||||
|
||||
async function runStatiCrypt() {
|
||||
// if the 's' flag is passed without parameter, generate a salt, display & exit
|
||||
if (isOptionSetByUser("s", yargs) && !namedArgs.salt) {
|
||||
|
@ -91,94 +81,32 @@ async function runStatiCrypt() {
|
|||
console.log(url + "#staticrypt_pwd=" + hashedPassword);
|
||||
}
|
||||
|
||||
// TODO: remove in the next major version bump. This is to allow a security update to some versions without breaking
|
||||
// older ones. If the password template is custom AND created before 2.2.0 we need to use the old hashing algorithm.
|
||||
const isLegacy = isCustomPasswordTemplateLegacy(namedArgs.f);
|
||||
|
||||
if (isLegacy) {
|
||||
console.log(
|
||||
"#################################\n\n" +
|
||||
"SECURITY WARNING [StatiCrypt]: You are using an old version of the password template, which has been found to " +
|
||||
"be less secure. Please update your custom password_template logic to match the latest version." +
|
||||
"\nYou can find instructions here: https://github.com/robinmoisson/staticrypt/issues/161" +
|
||||
"\n\n#################################"
|
||||
);
|
||||
}
|
||||
|
||||
if (!isWebcrypto) {
|
||||
console.log(
|
||||
"WARNING: If you are viewing the file over HTTPS or locally, we recommend " +
|
||||
(isPasswordTemplateUsingAsync(namedArgs.f) ? "" : "updating your password template to the latest version and ") +
|
||||
"using the '--engine webcrypto' more secure engine. It will become the default in StatiCrypt next major version."
|
||||
);
|
||||
} else if (!isPasswordTemplateUsingAsync(namedArgs.f) && isWebcrypto) {
|
||||
exitWithError(
|
||||
"The '--engine webcrypto' engine is only available for password templates that use async/await. Please " +
|
||||
"update your password template to the latest version or use the '--engine cryptojs' engine."
|
||||
)
|
||||
}
|
||||
|
||||
// create crypto-js tag (embedded or not)
|
||||
let cryptoTag = CRYPTOJS_SCRIPT_TAG;
|
||||
if (isWebcrypto) {
|
||||
cryptoTag = "";
|
||||
} else if (namedArgs.embed) {
|
||||
try {
|
||||
const embedContents = fs.readFileSync(
|
||||
path.join(__dirname, "..", "lib", "kryptojs-3.1.9-1.min.js"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
cryptoTag = "<script>" + embedContents + "</script>";
|
||||
} catch (e) {
|
||||
exitWithError("Embed file does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
const cryptoEngineString = isWebcrypto
|
||||
? convertCommonJSToBrowserJS("lib/cryptoEngine/webcryptoEngine")
|
||||
: convertCommonJSToBrowserJS("lib/cryptoEngine/cryptojsEngine");
|
||||
|
||||
// get the file content
|
||||
const contents = getFileContent(inputFilepath);
|
||||
|
||||
// encrypt input
|
||||
encode(contents, password, salt, isLegacy).then((encryptedMessage) => {
|
||||
let codecString;
|
||||
if (isWebcrypto) {
|
||||
codecString = convertCommonJSToBrowserJS("lib/codec");
|
||||
} else {
|
||||
// TODO: remove on next major version bump. The replace is a hack to pass the salt to the injected js_codec in
|
||||
// a backward compatible way (not requiring to update the password_template). Same for using a "sync" version
|
||||
// of the codec.
|
||||
codecString = convertCommonJSToBrowserJS("lib/codec-sync").replace('##SALT##', salt);
|
||||
}
|
||||
const encryptedMessage = await encode(contents, password, salt);
|
||||
|
||||
const data = {
|
||||
crypto_tag: cryptoTag,
|
||||
decrypt_button: namedArgs.decryptButton,
|
||||
// TODO: deprecated option here for backward compat, remove on next major version bump
|
||||
embed: isWebcrypto ? false : namedArgs.embed,
|
||||
encrypted: encryptedMessage,
|
||||
instructions: namedArgs.instructions,
|
||||
is_remember_enabled: namedArgs.noremember ? "false" : "true",
|
||||
js_codec: codecString,
|
||||
js_crypto_engine: cryptoEngineString,
|
||||
label_error: namedArgs.labelError,
|
||||
passphrase_placeholder: namedArgs.passphrasePlaceholder,
|
||||
remember_duration_in_days: namedArgs.remember,
|
||||
remember_me: namedArgs.rememberLabel,
|
||||
salt: salt,
|
||||
title: namedArgs.title,
|
||||
};
|
||||
const data = {
|
||||
decrypt_button: namedArgs.decryptButton,
|
||||
encrypted: encryptedMessage,
|
||||
instructions: namedArgs.instructions,
|
||||
is_remember_enabled: namedArgs.noremember ? "false" : "true",
|
||||
js_codec: convertCommonJSToBrowserJS("lib/codec"),
|
||||
js_crypto_engine: convertCommonJSToBrowserJS("lib/cryptoEngine"),
|
||||
label_error: namedArgs.labelError,
|
||||
passphrase_placeholder: namedArgs.passphrasePlaceholder,
|
||||
remember_duration_in_days: namedArgs.remember,
|
||||
remember_me: namedArgs.rememberLabel,
|
||||
salt: salt,
|
||||
title: namedArgs.title,
|
||||
};
|
||||
|
||||
const outputFilepath = namedArgs.output !== null
|
||||
? namedArgs.output
|
||||
: inputFilepath.replace(/\.html$/, "") + "_encrypted.html";
|
||||
const outputFilepath = namedArgs.output !== null
|
||||
? namedArgs.output
|
||||
: inputFilepath.replace(/\.html$/, "") + "_encrypted.html";
|
||||
|
||||
genFile(data, outputFilepath, namedArgs.f);
|
||||
|
||||
});
|
||||
genFile(data, outputFilepath, namedArgs.f);
|
||||
}
|
||||
|
||||
runStatiCrypt();
|
||||
|
|
|
@ -201,8 +201,6 @@ const ENCRYPTION_ALGO = "AES-CBC";
|
|||
/**
|
||||
* Translates between utf8 encoded hexadecimal strings
|
||||
* and Uint8Array bytes.
|
||||
*
|
||||
* Mirrors the API of CryptoJS.enc.Hex
|
||||
*/
|
||||
const HexEncoder = {
|
||||
/**
|
||||
|
@ -564,7 +562,7 @@ exports.init = init;
|
|||
const decode = codec.init(cryptoEngine).decode;
|
||||
|
||||
// variables to be filled when generating the file
|
||||
const encryptedMsg = 'df7428ed075decd3c4ff9d8ab6d2bea4410854c863d705789fb22e14b7da7ee20e94c593047abb9ba34d6519eebc879d2097bb918c0af0d4e248959849fb9c6bbb93aba054806c8773d1e4b63ec317185ad5462a9919dda986716c67bb57a89a044de3e25707cded482657c4a0208e9916aaa9d839f090eaaeb95603e05db11fe4bc37c4d98b9170124ce1c7ca18fe39c2f179e23eee61ba7d79cb3145e8833936c62adeffce1f5e129745c89541faa8100bfde4733bfa9c0ecf04768b3d1889',
|
||||
const encryptedMsg = '2c0a13159934226fa022225a06c64af6dfffe80dd6cb908f0f6ad93311563d78150cfc5465e3c7d70d682194a6d4a0c6082e37aaca8dbd83036d9e9cf629a112132b8fb6004a028b31ea4fb5a9f82d505096fe59e109970261733b4c8f21110b6f365d8b087d0ec15866917341e3cd105c65c9c7542626bae08903cd10675ed7fbd71062a1e87e35d30341c8251ab452352eaeecd6e44ed0a256979b30287032bccefaecf1685e948bf0fc3a3b5ad1f7b70092d9e32ceb60818ee49e821f8933',
|
||||
salt = 'b93bbaf35459951c47721d1f3eaeb5b9',
|
||||
labelError = 'Bad password!',
|
||||
isRememberEnabled = true,
|
||||
|
|
11
index.html
11
index.html
|
@ -184,11 +184,6 @@ Your encrypted string</pre>
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Filename changed to circumvent adblockers that mistake it for a crypto miner (see https://github.com/robinmoisson/staticrypt/issues/107)
|
||||
-->
|
||||
<script src="lib/kryptojs-3.1.9-1.min.js"></script>
|
||||
|
||||
<script src="https://cdn.ckeditor.com/4.7.0/standard/ckeditor.js"></script>
|
||||
|
||||
<script id="cryptoEngine">
|
||||
|
@ -203,8 +198,6 @@ const ENCRYPTION_ALGO = "AES-CBC";
|
|||
/**
|
||||
* Translates between utf8 encoded hexadecimal strings
|
||||
* and Uint8Array bytes.
|
||||
*
|
||||
* Mirrors the API of CryptoJS.enc.Hex
|
||||
*/
|
||||
const HexEncoder = {
|
||||
/**
|
||||
|
@ -615,8 +608,8 @@ exports.renderTemplate = renderTemplate;
|
|||
}
|
||||
|
||||
/**
|
||||
* Register something happened - this uses a simple Supabase function to implement a counter, and allows use to drop
|
||||
* google analytics.
|
||||
* Register something happened - this uses a simple Supabase function to implement a counter, and allows to drop
|
||||
* google analytics. We don't store any personal data or IP.
|
||||
*
|
||||
* @param action
|
||||
*/
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/**
|
||||
* TODO: delete this file in next major version. This is a version of the codec that doesn't use async, so we can use it
|
||||
* in old custom password_template. It will only be used with the cryptoJS engine.
|
||||
*
|
||||
* Initialize the codec with the provided cryptoEngine - this return functions to encode and decode messages.
|
||||
*
|
||||
* @param cryptoEngine - the engine to use for encryption / decryption
|
||||
*/
|
||||
function init(cryptoEngine) {
|
||||
// TODO: remove on next major version bump. This is a hack to make the salt available in all functions here in a
|
||||
// backward compatible way (not requiring to change the password_template).
|
||||
const backwardCompatibleSalt = '##SALT##';
|
||||
|
||||
const exports = {};
|
||||
|
||||
/**
|
||||
* Top-level function for encoding a message.
|
||||
* Includes password hashing, encryption, and signing.
|
||||
*
|
||||
* @param {string} msg
|
||||
* @param {string} password
|
||||
* @param {string} salt
|
||||
* @param {boolean} isLegacy - whether to use the legacy hashing algorithm (1k iterations) or not
|
||||
*
|
||||
* @returns {string} The encoded text
|
||||
*/
|
||||
function encode(msg, password, salt, isLegacy = false) {
|
||||
// TODO: remove in the next major version bump. This is to not break backwards compatibility with the old way of hashing
|
||||
const hashedPassphrase = isLegacy
|
||||
? cryptoEngine.hashLegacyRound(password, salt)
|
||||
: cryptoEngine.hashPassphrase(password, salt);
|
||||
const encrypted = cryptoEngine.encrypt(msg, hashedPassphrase);
|
||||
// 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 = cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||
|
||||
return hmac + encrypted;
|
||||
}
|
||||
exports.encode = encode;
|
||||
|
||||
/**
|
||||
* Top-level function for decoding a message.
|
||||
* Includes signature check and decryption.
|
||||
*
|
||||
* @param {string} signedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @param {string} backwardCompatibleHashedPassword
|
||||
*
|
||||
* @returns {Object} {success: true, decoded: string} | {success: false, message: string}
|
||||
*/
|
||||
function decode(signedMsg, hashedPassphrase, backwardCompatibleHashedPassword = '') {
|
||||
const encryptedHMAC = signedMsg.substring(0, 64);
|
||||
const encryptedMsg = signedMsg.substring(64);
|
||||
const decryptedHMAC = cryptoEngine.signMessage(hashedPassphrase, encryptedMsg);
|
||||
|
||||
if (decryptedHMAC !== encryptedHMAC) {
|
||||
// TODO: remove in next major version bump. This is to not break backwards compatibility with the old 1k
|
||||
// iterations in PBKDF2 - if the key we try isn't working, it might be because it's a remember-me/autodecrypt
|
||||
// link key, generated with 1k iterations. Try again with the updated iteration count.
|
||||
if (!backwardCompatibleHashedPassword) {
|
||||
return decode(
|
||||
signedMsg,
|
||||
cryptoEngine.hashSecondRound(hashedPassphrase, backwardCompatibleSalt),
|
||||
hashedPassphrase
|
||||
);
|
||||
}
|
||||
|
||||
return { success: false, message: "Signature mismatch" };
|
||||
}
|
||||
|
||||
// TODO: remove in next major version bump. If we're trying to double hash for backward compatibility reasons,
|
||||
// and the attempt is successful, we check if we should update the stored password in localStorage. This avoids
|
||||
// having to compute the upgrade each time.
|
||||
if (backwardCompatibleHashedPassword) {
|
||||
if (window && window.localStorage) {
|
||||
const storedPassword = window.localStorage.getItem('staticrypt_passphrase');
|
||||
|
||||
// check the stored password is actually the backward compatible one, so we don't save the new one and trigger
|
||||
// the "remember-me" by mistake, leaking the password
|
||||
if (storedPassword === backwardCompatibleHashedPassword) {
|
||||
window.localStorage.setItem('staticrypt_passphrase', hashedPassphrase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
decoded: cryptoEngine.decrypt(encryptedMsg, hashedPassphrase),
|
||||
};
|
||||
}
|
||||
exports.decode = decode;
|
||||
|
||||
return exports;
|
||||
}
|
||||
exports.init = init;
|
|
@ -8,8 +8,6 @@ const ENCRYPTION_ALGO = "AES-CBC";
|
|||
/**
|
||||
* Translates between utf8 encoded hexadecimal strings
|
||||
* and Uint8Array bytes.
|
||||
*
|
||||
* Mirrors the API of CryptoJS.enc.Hex
|
||||
*/
|
||||
const HexEncoder = {
|
||||
/**
|
|
@ -1,138 +0,0 @@
|
|||
const CryptoJS = require("crypto-js");
|
||||
|
||||
/**
|
||||
* Salt and encrypt a msg with a password.
|
||||
*/
|
||||
function encrypt(msg, hashedPassphrase) {
|
||||
var iv = CryptoJS.lib.WordArray.random(128 / 8);
|
||||
|
||||
var encrypted = CryptoJS.AES.encrypt(msg, hashedPassphrase, {
|
||||
iv: iv,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
mode: CryptoJS.mode.CBC,
|
||||
});
|
||||
|
||||
// iv will be hex 16 in length (32 characters)
|
||||
// we prepend it to the ciphertext for use in decryption
|
||||
return iv.toString() + encrypted.toString();
|
||||
}
|
||||
exports.encrypt = encrypt;
|
||||
|
||||
/**
|
||||
* Decrypt a salted msg using a password.
|
||||
*
|
||||
* @param {string} encryptedMsg
|
||||
* @param {string} hashedPassphrase
|
||||
* @returns {string}
|
||||
*/
|
||||
function decrypt(encryptedMsg, hashedPassphrase) {
|
||||
var iv = CryptoJS.enc.Hex.parse(encryptedMsg.substr(0, 32));
|
||||
var encrypted = encryptedMsg.substring(32);
|
||||
|
||||
return CryptoJS.AES.decrypt(encrypted, hashedPassphrase, {
|
||||
iv: iv,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
mode: CryptoJS.mode.CBC,
|
||||
}).toString(CryptoJS.enc.Utf8);
|
||||
}
|
||||
exports.decrypt = decrypt;
|
||||
|
||||
/**
|
||||
* Salt and hash the passphrase so it can be stored in localStorage without opening a password reuse vulnerability.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} salt
|
||||
* @returns string
|
||||
*/
|
||||
function hashPassphrase(passphrase, salt) {
|
||||
// we hash the passphrase in two steps: first 1k iterations, then we add 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
|
||||
var hashedPassphrase = hashLegacyRound(passphrase, salt);
|
||||
|
||||
return hashSecondRound(hashedPassphrase, salt);
|
||||
}
|
||||
exports.hashPassphrase = hashPassphrase;
|
||||
|
||||
/**
|
||||
* This hashes the passphrase with 1k iterations. This is a low number, we need this function to support backwards
|
||||
* compatibility.
|
||||
*
|
||||
* @param {string} passphrase
|
||||
* @param {string} salt
|
||||
* @returns {string}
|
||||
*/
|
||||
function hashLegacyRound(passphrase, salt) {
|
||||
return CryptoJS.PBKDF2(passphrase, salt, {
|
||||
keySize: 256 / 32,
|
||||
iterations: 1000,
|
||||
}).toString();
|
||||
}
|
||||
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 salt
|
||||
* @returns {string}
|
||||
*/
|
||||
function hashSecondRound(hashedPassphrase, salt) {
|
||||
return CryptoJS.PBKDF2(hashedPassphrase, salt, {
|
||||
keySize: 256 / 32,
|
||||
iterations: 14000,
|
||||
hasher: CryptoJS.algo.SHA256,
|
||||
}).toString();
|
||||
}
|
||||
exports.hashSecondRound = hashSecondRound;
|
||||
|
||||
function generateRandomSalt() {
|
||||
return CryptoJS.lib.WordArray.random(128 / 8).toString();
|
||||
}
|
||||
exports.generateRandomSalt = generateRandomSalt;
|
||||
|
||||
function getRandomAlphanum() {
|
||||
var possibleCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
var byteArray;
|
||||
var parsedInt;
|
||||
|
||||
// Keep generating new random bytes until we get a value that falls
|
||||
// within a range that can be evenly divided by possibleCharacters.length
|
||||
do {
|
||||
byteArray = CryptoJS.lib.WordArray.random(1);
|
||||
// extract the lowest byte to get an int from 0 to 255 (probably unnecessary, since we're only generating 1 byte)
|
||||
parsedInt = byteArray.words[0] & 0xff;
|
||||
} while (parsedInt >= 256 - (256 % possibleCharacters.length));
|
||||
|
||||
// Take the modulo of the parsed integer to get a random number between 0 and totalLength - 1
|
||||
var randomIndex = parsedInt % possibleCharacters.length;
|
||||
|
||||
return possibleCharacters[randomIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random string of a given length.
|
||||
*
|
||||
* @param {int} length
|
||||
* @returns {string}
|
||||
*/
|
||||
function generateRandomString(length) {
|
||||
var randomString = '';
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
randomString += getRandomAlphanum();
|
||||
}
|
||||
|
||||
return randomString;
|
||||
}
|
||||
exports.generateRandomString = generateRandomString;
|
||||
|
||||
function signMessage(hashedPassphrase, message) {
|
||||
return CryptoJS.HmacSHA256(
|
||||
message,
|
||||
CryptoJS.SHA256(hashedPassphrase).toString()
|
||||
).toString();
|
||||
}
|
||||
exports.signMessage = signMessage;
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "staticrypt",
|
||||
"version": "2.6.0",
|
||||
"version": "3.0.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).",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
|
|
@ -2,7 +2,7 @@ const { convertCommonJSToBrowserJS, genFile } = require("../cli/helpers.js");
|
|||
|
||||
const data = {
|
||||
js_codec: convertCommonJSToBrowserJS("lib/codec"),
|
||||
js_crypto_engine: convertCommonJSToBrowserJS("lib/cryptoEngine/webcryptoEngine"),
|
||||
js_crypto_engine: convertCommonJSToBrowserJS("lib/cryptoEngine"),
|
||||
js_formater: convertCommonJSToBrowserJS("lib/formater"),
|
||||
};
|
||||
|
||||
|
|
|
@ -184,11 +184,6 @@ Your encrypted string</pre>
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Filename changed to circumvent adblockers that mistake it for a crypto miner (see https://github.com/robinmoisson/staticrypt/issues/107)
|
||||
-->
|
||||
<script src="lib/kryptojs-3.1.9-1.min.js"></script>
|
||||
|
||||
<script src="https://cdn.ckeditor.com/4.7.0/standard/ckeditor.js"></script>
|
||||
|
||||
<script id="cryptoEngine">
|
||||
|
@ -223,8 +218,8 @@ Filename changed to circumvent adblockers that mistake it for a crypto miner (se
|
|||
}
|
||||
|
||||
/**
|
||||
* Register something happened - this uses a simple Supabase function to implement a counter, and allows use to drop
|
||||
* google analytics.
|
||||
* Register something happened - this uses a simple Supabase function to implement a counter, and allows to drop
|
||||
* google analytics. We don't store any personal data or IP.
|
||||
*
|
||||
* @param {string} action
|
||||
*/
|
||||
|
|
Ładowanie…
Reference in New Issue