kopia lustrzana https://github.com/robinmoisson/staticrypt
98 wiersze
3.9 KiB
JavaScript
98 wiersze
3.9 KiB
JavaScript
/**
|
|
* 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) {
|
|
const exports = {};
|
|
|
|
/**
|
|
* Top-level function for encoding a message.
|
|
* Includes password hashing, encryption, and signing.
|
|
*
|
|
* @param {string} msg
|
|
* @param {string} password
|
|
* @param {string} salt
|
|
*
|
|
* @returns {string} The encoded text
|
|
*/
|
|
async function encode(msg, password, salt) {
|
|
const hashedPassword = await cryptoEngine.hashPassword(password, salt);
|
|
|
|
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(hashedPassword, encrypted);
|
|
|
|
return hmac + encrypted;
|
|
}
|
|
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} hashedPassword
|
|
*
|
|
* @returns {string} The encoded text
|
|
*/
|
|
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(hashedPassword, encrypted);
|
|
|
|
return hmac + encrypted;
|
|
}
|
|
exports.encodeWithHashedPassword = encodeWithHashedPassword;
|
|
|
|
/**
|
|
* Top-level function for decoding a message.
|
|
* Includes signature check and decryption.
|
|
*
|
|
* @param {string} signedMsg
|
|
* @param {string} hashedPassword
|
|
* @param {string} salt
|
|
* @param {int} backwardCompatibleAttempt
|
|
* @param {string} originalPassword
|
|
*
|
|
* @returns {Object} {success: true, decoded: string} | {success: false, message: string}
|
|
*/
|
|
async function decode(signedMsg, hashedPassword, salt, backwardCompatibleAttempt = 0, originalPassword = "") {
|
|
const encryptedHMAC = signedMsg.substring(0, 64);
|
|
const encryptedMsg = signedMsg.substring(64);
|
|
const decryptedHMAC = await cryptoEngine.signMessage(hashedPassword, encryptedMsg);
|
|
|
|
if (decryptedHMAC !== encryptedHMAC) {
|
|
// 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.
|
|
originalPassword = originalPassword || hashedPassword;
|
|
if (backwardCompatibleAttempt === 0) {
|
|
const updatedHashedPassword = await cryptoEngine.hashThirdRound(originalPassword, salt);
|
|
|
|
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
|
}
|
|
if (backwardCompatibleAttempt === 1) {
|
|
let updatedHashedPassword = await cryptoEngine.hashSecondRound(originalPassword, salt);
|
|
updatedHashedPassword = await cryptoEngine.hashThirdRound(updatedHashedPassword, salt);
|
|
|
|
return decode(signedMsg, updatedHashedPassword, salt, backwardCompatibleAttempt + 1, originalPassword);
|
|
}
|
|
|
|
return { success: false, message: "Signature mismatch" };
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
decoded: await cryptoEngine.decrypt(encryptedMsg, hashedPassword),
|
|
};
|
|
}
|
|
exports.decode = decode;
|
|
|
|
return exports;
|
|
}
|
|
exports.init = init;
|