kopia lustrzana https://github.com/robinmoisson/staticrypt
support encrypting multiple files
rodzic
5a8425387a
commit
4696a24324
|
@ -47,10 +47,12 @@ staticrypt test.html -p MY_LONG_PASSWORD --share https://example.com/test_encryp
|
||||||
# => https://example.com/test_encrypted.html#staticrypt_pwd=5bfbf1343c7257cd7be23ecd74bb37fa2c76d041042654f358b6255baeab898f
|
# => https://example.com/test_encrypted.html#staticrypt_pwd=5bfbf1343c7257cd7be23ecd74bb37fa2c76d041042654f358b6255baeab898f
|
||||||
```
|
```
|
||||||
|
|
||||||
**Encrypt all html files from a directory** and put them in a `encrypted/` directory (`{}` will be replaced with each file name by the `find` command):
|
**Encrypt multiple files at once** and put them in a `encrypted/` directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
find . -type f -name "*.html" -exec staticrypt {} -p MY_LONG_PASSWORD \;
|
# this will encrypt test_A.html, test_B.html and all files in the test/ directory
|
||||||
|
staticrypt test_A.html test_B.html test/* -p MY_LONG_PASSWORD
|
||||||
|
# => encrypted files are in encrypted/test_A.html, encrypted/test_B.html, encrypted/test/...
|
||||||
```
|
```
|
||||||
|
|
||||||
**Pin the salt to use staticrypt in your CI in a build step** - if you want want the "Remember-me" or share features to work accross multiple pages or multiple successive deployment, the salt needs to stay the same ([see why](https://github.com/robinmoisson/staticrypt#why-does-staticrypt-create-a-config-file)). If you run StatiCrypt in a CI step, you can pin the salt in two ways:
|
**Pin the salt to use staticrypt in your CI in a build step** - if you want want the "Remember-me" or share features to work accross multiple pages or multiple successive deployment, the salt needs to stay the same ([see why](https://github.com/robinmoisson/staticrypt#why-does-staticrypt-create-a-config-file)). If you run StatiCrypt in a CI step, you can pin the salt in two ways:
|
||||||
|
@ -68,7 +70,7 @@ staticrypt test.html -p MY_LONG_PASSWORD --salt 12345678901234567890123456789012
|
||||||
|
|
||||||
The password argument is optional if `STATICRYPT_PASSWORD` is set in the environment or `.env` file.
|
The password argument is optional if `STATICRYPT_PASSWORD` is set in the environment or `.env` file.
|
||||||
|
|
||||||
Usage: staticrypt <filename> [options]
|
Usage: staticrypt <filename> [<filename> ...] [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--help Show help [boolean]
|
--help Show help [boolean]
|
||||||
|
|
|
@ -146,7 +146,7 @@ function getFileContent(filepath) {
|
||||||
try {
|
try {
|
||||||
return fs.readFileSync(filepath, "utf8");
|
return fs.readFileSync(filepath, "utf8");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exitWithError("input file does not exist!");
|
exitWithError(`input file '${filepath}' does not exist!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.getFileContent = getFileContent;
|
exports.getFileContent = getFileContent;
|
||||||
|
@ -289,7 +289,7 @@ function isCustomPasswordTemplateDefault(templatePathParameter) {
|
||||||
exports.isCustomPasswordTemplateDefault = isCustomPasswordTemplateDefault;
|
exports.isCustomPasswordTemplateDefault = isCustomPasswordTemplateDefault;
|
||||||
|
|
||||||
function parseCommandLineArguments() {
|
function parseCommandLineArguments() {
|
||||||
return Yargs.usage("Usage: staticrypt <filename> [options]")
|
return Yargs.usage("Usage: staticrypt <filename> [<filename> ...] [options]")
|
||||||
.option("c", {
|
.option("c", {
|
||||||
alias: "config",
|
alias: "config",
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|
46
cli/index.js
46
cli/index.js
|
@ -15,7 +15,7 @@ require('dotenv').config();
|
||||||
const cryptoEngine = require("../lib/cryptoEngine.js");
|
const cryptoEngine = require("../lib/cryptoEngine.js");
|
||||||
const codec = require("../lib/codec.js");
|
const codec = require("../lib/codec.js");
|
||||||
const { generateRandomSalt } = cryptoEngine;
|
const { generateRandomSalt } = cryptoEngine;
|
||||||
const { encode } = codec.init(cryptoEngine);
|
const { encodeWithHashedPassphrase } = codec.init(cryptoEngine);
|
||||||
const { parseCommandLineArguments, buildStaticryptJS, isOptionSetByUser, genFile, getFileContent,
|
const { parseCommandLineArguments, buildStaticryptJS, isOptionSetByUser, genFile, getFileContent,
|
||||||
getValidatedSalt,
|
getValidatedSalt,
|
||||||
getValidatedPassword, getConfig, writeConfig
|
getValidatedPassword, getConfig, writeConfig
|
||||||
|
@ -31,7 +31,7 @@ async function runStatiCrypt() {
|
||||||
|
|
||||||
const positionalArguments = namedArgs._;
|
const positionalArguments = namedArgs._;
|
||||||
|
|
||||||
// validate the number of arguments
|
// require at least one positional argument unless some specific flags are passed
|
||||||
if (!hasShareFlag && !(hasSaltFlag && !namedArgs.salt)) {
|
if (!hasShareFlag && !(hasSaltFlag && !namedArgs.salt)) {
|
||||||
if (positionalArguments.length === 0) {
|
if (positionalArguments.length === 0) {
|
||||||
console.log("ERROR: Invalid number of arguments. Please provide an input file.\n");
|
console.log("ERROR: Invalid number of arguments. Please provide an input file.\n");
|
||||||
|
@ -81,24 +81,11 @@ async function runStatiCrypt() {
|
||||||
writeConfig(configPath, config);
|
writeConfig(configPath, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the file content
|
|
||||||
const inputFilepath = positionalArguments[0].toString();
|
|
||||||
const contents = getFileContent(inputFilepath);
|
|
||||||
|
|
||||||
// encrypt input
|
|
||||||
const encryptedMsg = await encode(contents, password, salt);
|
|
||||||
|
|
||||||
const isRememberEnabled = namedArgs.remember !== "false";
|
const isRememberEnabled = namedArgs.remember !== "false";
|
||||||
|
|
||||||
const data = {
|
const baseTemplateData = {
|
||||||
is_remember_enabled: JSON.stringify(isRememberEnabled),
|
is_remember_enabled: JSON.stringify(isRememberEnabled),
|
||||||
js_staticrypt: buildStaticryptJS(),
|
js_staticrypt: buildStaticryptJS(),
|
||||||
staticrypt_config: {
|
|
||||||
encryptedMsg,
|
|
||||||
isRememberEnabled,
|
|
||||||
rememberDurationInDays: namedArgs.remember,
|
|
||||||
salt,
|
|
||||||
},
|
|
||||||
template_button: namedArgs.templateButton,
|
template_button: namedArgs.templateButton,
|
||||||
template_error: namedArgs.templateError,
|
template_error: namedArgs.templateError,
|
||||||
template_instructions: namedArgs.templateInstructions,
|
template_instructions: namedArgs.templateInstructions,
|
||||||
|
@ -109,9 +96,32 @@ async function runStatiCrypt() {
|
||||||
template_color_secondary: namedArgs.templateColorSecondary,
|
template_color_secondary: namedArgs.templateColorSecondary,
|
||||||
};
|
};
|
||||||
|
|
||||||
const outputFilepath = namedArgs.directory.replace(/\/+$/, '') + "/" + inputFilepath;
|
const hashedPassword = await cryptoEngine.hashPassphrase(password, salt);
|
||||||
|
|
||||||
genFile(data, outputFilepath, namedArgs.template);
|
for (const positionalArgument of positionalArguments) {
|
||||||
|
const inputFilepath = positionalArgument.toString();
|
||||||
|
|
||||||
|
// get the file content
|
||||||
|
const contents = getFileContent(inputFilepath);
|
||||||
|
|
||||||
|
// encrypt input
|
||||||
|
const encryptedMsg = await encodeWithHashedPassphrase(contents, hashedPassword);
|
||||||
|
|
||||||
|
const staticryptConfig = {
|
||||||
|
encryptedMsg,
|
||||||
|
isRememberEnabled,
|
||||||
|
rememberDurationInDays: namedArgs.remember,
|
||||||
|
salt,
|
||||||
|
};
|
||||||
|
const templateData = {
|
||||||
|
...baseTemplateData,
|
||||||
|
staticrypt_config: staticryptConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
const outputFilepath = namedArgs.directory.replace(/\/+$/, '') + "/" + inputFilepath;
|
||||||
|
|
||||||
|
genFile(templateData, outputFilepath, namedArgs.template);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runStatiCrypt();
|
runStatiCrypt();
|
||||||
|
|
|
@ -488,8 +488,8 @@ function init(cryptoEngine) {
|
||||||
async function encode(msg, password, salt) {
|
async function encode(msg, password, salt) {
|
||||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||||
|
|
||||||
|
|
||||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
const encrypted = await 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
|
// 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)
|
// it in localStorage safely, we don't use the clear text password)
|
||||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
@ -498,6 +498,26 @@ function init(cryptoEngine) {
|
||||||
}
|
}
|
||||||
exports.encode = encode;
|
exports.encode = encode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode using a password that has already been hashed. This is useful to encode multiple messages in a row, that way
|
||||||
|
* we don't need to hash the password multiple times.
|
||||||
|
*
|
||||||
|
* @param {string} msg
|
||||||
|
* @param {string} hashedPassphrase
|
||||||
|
*
|
||||||
|
* @returns {string} The encoded text
|
||||||
|
*/
|
||||||
|
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||||
|
const encrypted = await 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 = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
|
||||||
|
return hmac + encrypted;
|
||||||
|
}
|
||||||
|
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-level function for decoding a message.
|
* Top-level function for decoding a message.
|
||||||
* Includes signature check and decryption.
|
* Includes signature check and decryption.
|
||||||
|
@ -770,7 +790,7 @@ exports.init = init;
|
||||||
})())
|
})())
|
||||||
const templateError = 'Bad password!',
|
const templateError = 'Bad password!',
|
||||||
isRememberEnabled = true,
|
isRememberEnabled = true,
|
||||||
staticryptConfig = {"encryptedMsg":"87f21fbcd8377a4add0382f281123d5f3d7422d99eeac1e8bf287931f307ab42ed795ce671de90d2127951422f748d7f3b93e711e4e0ddf450371c741031208ae7a9c7a7cc38b7969c86dc22beb869ce9b98aa1abd29d2dd658b2e163729341fe247d79e15e172c716e4d99bc7e7b7581a648bc7f87a0639c497b299d2f2fe2b85dd43e8ac7018ef3d0028a39fb3d156ae0f4e303b575e70c4fb0079f353f0510798816b9009a4156d7803b286db1c82e55e68eb2b280be7fb20e4c01dc417f0","isRememberEnabled":true,"rememberDurationInDays":0,"salt":"b93bbaf35459951c47721d1f3eaeb5b9"};
|
staticryptConfig = {"encryptedMsg":"8ad22b42e255e1467bbd5369c84ae7420583384946876422f9450a5ab7fb3eaf33f5b73b1980988ef451c513871bb24582fabe01c8a5d0bde2a0580d564bf9c232cffc7fca5169005c024de005a25f479c172a65f6554405874082906b4a3be1ca7c0118728b25cd3ae915277e19f574c4f794affeffb2a1c3e923ea12fd83f5005a1c1b9606d36448082bd0f8ef70f8cc99f5205075e702b2f2b795c9150e20f2de2c6c86631c2d59ebbe2543c8ec13e450b21bbefdc2f2cb219190a0510538","isRememberEnabled":true,"rememberDurationInDays":0,"salt":"b93bbaf35459951c47721d1f3eaeb5b9"};
|
||||||
|
|
||||||
// you can edit these values to customize some of the behavior of StatiCrypt
|
// you can edit these values to customize some of the behavior of StatiCrypt
|
||||||
const templateConfig = {
|
const templateConfig = {
|
||||||
|
|
44
index.html
44
index.html
|
@ -494,8 +494,8 @@ function init(cryptoEngine) {
|
||||||
async function encode(msg, password, salt) {
|
async function encode(msg, password, salt) {
|
||||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||||
|
|
||||||
|
|
||||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
const encrypted = await 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
|
// 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)
|
// it in localStorage safely, we don't use the clear text password)
|
||||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
@ -504,6 +504,26 @@ function init(cryptoEngine) {
|
||||||
}
|
}
|
||||||
exports.encode = encode;
|
exports.encode = encode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode using a password that has already been hashed. This is useful to encode multiple messages in a row, that way
|
||||||
|
* we don't need to hash the password multiple times.
|
||||||
|
*
|
||||||
|
* @param {string} msg
|
||||||
|
* @param {string} hashedPassphrase
|
||||||
|
*
|
||||||
|
* @returns {string} The encoded text
|
||||||
|
*/
|
||||||
|
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||||
|
const encrypted = await 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 = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
|
||||||
|
return hmac + encrypted;
|
||||||
|
}
|
||||||
|
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-level function for decoding a message.
|
* Top-level function for decoding a message.
|
||||||
* Includes signature check and decryption.
|
* Includes signature check and decryption.
|
||||||
|
@ -903,8 +923,8 @@ function init(cryptoEngine) {
|
||||||
async function encode(msg, password, salt) {
|
async function encode(msg, password, salt) {
|
||||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||||
|
|
||||||
|
|
||||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
const encrypted = await 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
|
// 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)
|
// it in localStorage safely, we don't use the clear text password)
|
||||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
@ -913,6 +933,26 @@ function init(cryptoEngine) {
|
||||||
}
|
}
|
||||||
exports.encode = encode;
|
exports.encode = encode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode using a password that has already been hashed. This is useful to encode multiple messages in a row, that way
|
||||||
|
* we don't need to hash the password multiple times.
|
||||||
|
*
|
||||||
|
* @param {string} msg
|
||||||
|
* @param {string} hashedPassphrase
|
||||||
|
*
|
||||||
|
* @returns {string} The encoded text
|
||||||
|
*/
|
||||||
|
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||||
|
const encrypted = await 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 = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
|
||||||
|
return hmac + encrypted;
|
||||||
|
}
|
||||||
|
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-level function for decoding a message.
|
* Top-level function for decoding a message.
|
||||||
* Includes signature check and decryption.
|
* Includes signature check and decryption.
|
||||||
|
|
22
lib/codec.js
22
lib/codec.js
|
@ -19,8 +19,8 @@ function init(cryptoEngine) {
|
||||||
async function encode(msg, password, salt) {
|
async function encode(msg, password, salt) {
|
||||||
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
const hashedPassphrase = await cryptoEngine.hashPassphrase(password, salt);
|
||||||
|
|
||||||
|
|
||||||
const encrypted = await cryptoEngine.encrypt(msg, hashedPassphrase);
|
const encrypted = await 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
|
// 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)
|
// it in localStorage safely, we don't use the clear text password)
|
||||||
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
const hmac = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
@ -29,6 +29,26 @@ function init(cryptoEngine) {
|
||||||
}
|
}
|
||||||
exports.encode = encode;
|
exports.encode = encode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode using a password that has already been hashed. This is useful to encode multiple messages in a row, that way
|
||||||
|
* we don't need to hash the password multiple times.
|
||||||
|
*
|
||||||
|
* @param {string} msg
|
||||||
|
* @param {string} hashedPassphrase
|
||||||
|
*
|
||||||
|
* @returns {string} The encoded text
|
||||||
|
*/
|
||||||
|
async function encodeWithHashedPassphrase(msg, hashedPassphrase) {
|
||||||
|
const encrypted = await 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 = await cryptoEngine.signMessage(hashedPassphrase, encrypted);
|
||||||
|
|
||||||
|
return hmac + encrypted;
|
||||||
|
}
|
||||||
|
exports.encodeWithHashedPassphrase = encodeWithHashedPassphrase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-level function for decoding a message.
|
* Top-level function for decoding a message.
|
||||||
* Includes signature check and decryption.
|
* Includes signature check and decryption.
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "staticrypt",
|
"name": "staticrypt",
|
||||||
"version": "3.1.1",
|
"version": "3.2.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "staticrypt",
|
"name": "staticrypt",
|
||||||
"version": "3.1.1",
|
"version": "3.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "staticrypt",
|
"name": "staticrypt",
|
||||||
"version": "3.1.1",
|
"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 passphrase and put it in a HTML file with a password prompt that can decrypted in-browser (client side).",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
|
Ładowanie…
Reference in New Issue