79221158

Date: 2024-11-24 22:00:44
Score: 0.5
Natty:
Report link

An update to @saptarshi-basu answer,

  1. Buffer.slice is now deprecated and replaced with Buffer.subarray

  2. The salt can also be added to the encrypted buffer array to help in the decryption process later

So a javascript implementation becomes

//--------------------------------------------------
//  INCLUDES
//--------------------------------------------------
    const crypto = require('crypto');
    const { Buffer } = require("buffer");
//--------------------------------------------------



//-----------------------------------------
//      module exports
//-----------------------------------------
    module.exports = {  encryptVal,
                        decryptVal
                    };
//-----------------------------------------



//--------------------------------------------------
//  CONSTANTS
//
//--------------------------------------------------
    const ENCRYPTION_CONSTANTS = {
        ALGORITHM:              'aes-256-gcm',  //--the algorithm used for encryption
        KEY_BYTE_LENGTH:        32,             //--the length of the key used for encryption
        IV_BYTE_LENGTH:         16,             //--the length of the initialization vector
        SALT_BYTE_LENGTH:       16,             //--the length of the salt used for encryption
        AUTH_TAG_BYTE_LENGTH:   16,             //--the length of the authentication tag
        INPUT_ENCODING:         'utf-8',        //--the encoding of the input data
        OUTPUT_ENCODING:        'base64url',    //--the encoding of the output data in base64url (url/cookies friendly)

        //OUTPUT_ENCODING:      'base64',       //--the encoding of the output data in base64
        //OUTPUT_ENCODING:      'hex',          //--the encoding of the output data in hex
    }
//--------------------------------------------------


//--------------------------------------------------


/**
 * This function is use to generate a random key
 * for the encryption process.
 * 
 * @returns {Buffer} the generated random key
 */
async function getRandomKey() {
    return crypto.randomBytes(ENCRYPTION_CONSTANTS.KEY_BYTE_LENGTH);
}



/**
 * This function is use to generate a key base
 * on the given password and salt for the 
 * encryption process.
 * 
 * @returns {Buffer} the generated random key
 */
async function getKeyFromPassword(gPassword, gSalt){
    return crypto.scryptSync(gPassword, gSalt, ENCRYPTION_CONSTANTS.KEY_BYTE_LENGTH);
}


/**
 * This function is use to generate a random salt
 * for the encryption process.
 * 
 * @returns {Buffer} the generated random salt
 */
async function getSalt(){
    return crypto.randomBytes(ENCRYPTION_CONSTANTS.SALT_BYTE_LENGTH);
}


/**
 * This function is use to generate a random salt
 * for the encryption process.
 * 
 * @returns {Buffer} the generated random salt
 */
async function getInitializationVector(){
    return crypto.randomBytes(ENCRYPTION_CONSTANTS.IV_BYTE_LENGTH);
}


/**
 * This function is use to encrypt a given value using
 * the given password.
 * 
 * @param {Buffer} gVal the value to be encrypted
 * @param {Buffer} gPassword the password to be used for encryption
 * 
 * @returns {Buffer} the encrypted value
 */ 
async function encryptVal(gVal, gPassword){

    try{
        const algorithm = ENCRYPTION_CONSTANTS.ALGORITHM;
        
        const iv = await getInitializationVector();
        const salt = await getSalt();
        const key = await getKeyFromPassword(gPassword, salt);

        const cipher = crypto.createCipheriv(algorithm, key, iv, {
            authTagLength: ENCRYPTION_CONSTANTS.AUTH_TAG_BYTE_LENGTH
        });

        const encryptedResults = Buffer.concat([cipher.update(gVal, ENCRYPTION_CONSTANTS.INPUT_ENCODING), cipher.final()]);

        return Buffer.concat([iv, salt, encryptedResults, cipher.getAuthTag()])
                 .toString(ENCRYPTION_CONSTANTS.OUTPUT_ENCODING);
                 
    }catch(err){

        //--log error to the system
        const errMsg = '--->>ERROR: `encryptVal()` error: '+err;
        console.log(errMsg);
    }

}


/**
 * This function is use to decrypt a given encrypted 
 * value using the given password.
 * 
 * @param {Buffer} gEncryptedVal the value to be decrypted
 * @param {Buffer} gPassword the password to be used for decryption
 * 
 * @returns {Buffer} the decrypted value
 */ 
async function decryptVal(gEncryptedVal, gPassword){

    try{
        const algorithm = ENCRYPTION_CONSTANTS.ALGORITHM;
        const encryptedBuffer = Buffer.from(gEncryptedVal, ENCRYPTION_CONSTANTS.OUTPUT_ENCODING); 
        
        const iv = encryptedBuffer.subarray(0, ENCRYPTION_CONSTANTS.IV_BYTE_LENGTH);
        const salt = encryptedBuffer.subarray(ENCRYPTION_CONSTANTS.IV_BYTE_LENGTH, ENCRYPTION_CONSTANTS.IV_BYTE_LENGTH + ENCRYPTION_CONSTANTS.SALT_BYTE_LENGTH);
        const encryptedData = encryptedBuffer.subarray((ENCRYPTION_CONSTANTS.IV_BYTE_LENGTH + ENCRYPTION_CONSTANTS.SALT_BYTE_LENGTH), -ENCRYPTION_CONSTANTS.AUTH_TAG_BYTE_LENGTH);
        const authTag = encryptedBuffer.subarray(-ENCRYPTION_CONSTANTS.AUTH_TAG_BYTE_LENGTH);

        const key = await getKeyFromPassword(gPassword, salt);

        const decipher = crypto.createDecipheriv(algorithm, key, iv, {
            authTagLength: ENCRYPTION_CONSTANTS.AUTH_TAG_BYTE_LENGTH
        });

        decipher.setAuthTag(authTag);

        return Buffer.concat([decipher.update(encryptedData), decipher.final()])
                     .toString(ENCRYPTION_CONSTANTS.INPUT_ENCODING);
    
    }catch(err){

        //--log error to the system
        const errMsg = '--->>ERROR: `decryptVal()` error: '+err;
        console.log(errMsg);
    }

}

You can try the functions above as:

    //--test encrypt and decrypt helper functions
    async function testEncryptDecrypt(){

        const txt = 'Hello World';
        const password = "opoo";

        const encryptedTxt = await encryptVal(txt, password);
        const decryptedTxt = await decryptVal(encryptedTxt, password);

        console.log('-->>OUTPUT: the encrypted text is: '+encryptedTxt);
        console.log('-->>OUTPUT: the decrypted text is: '+decryptedTxt);
    }

    testEncryptDecrypt();

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @saptarshi-basu
  • Low reputation (1):
Posted by: Oposinii