im trying to decrypt data encrypted from c++ using chacha20 but nodejs crypto doesnt support it
then im using the jschacha20 lib

but the nodejs weakDecrypt function decrypted data is wrong
whats wrong


bool weakEncrypt(QByteArray& data)
{
    if (data.isEmpty()) 
        return false;
    
    // Generate a salt from the password
    unsigned char pwhash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), pwhash);
    std::vector<unsigned char> salt(pwhash, pwhash + 32);

    constexpr size_t NONCE_SIZE = 12;
    constexpr size_t KEY_SIZE = 32;

    // Derive key from password
    unsigned char key[KEY_SIZE];
    SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), key);

    // Derive nonce
    std::vector<unsigned char> context;
    context.reserve(password.size() + salt.size());
    context.insert(context.end(), password.begin(), password.end());
    context.insert(context.end(), salt.begin(), salt.end());
    
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(context.data(), context.size(), hash);
    std::vector<unsigned char> nonce(hash, hash + NONCE_SIZE);
    
	qq << "\nkey\n"     << QByteArray::fromStdString(std::string(reinterpret_cast<char*>(key), sizeof(key))).toBase64();
	qq << "\nnonce\n"   << QByteArray::fromStdString(std::string(reinterpret_cast<char*>(nonce.data()), nonce.size())).toBase64();
	qq << "\nsalt\n"    << QByteArray::fromStdString(std::string(reinterpret_cast<char*>(salt.data()), salt.size())).toBase64();
	qq << "\ncontext\n" << QByteArray::fromStdString(std::string(reinterpret_cast<char*>(context.data()), context.size())).toBase64();

    // Encrypt with derived nonce
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) return false;
    if (EVP_EncryptInit_ex(ctx, EVP_chacha20(), nullptr, key, nonce.data()) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    QByteArray ciphertext(data.size(), 0);
    int ciphertextLen;
    if (EVP_EncryptUpdate(ctx, reinterpret_cast<unsigned char*>(ciphertext.data()), &ciphertextLen, 
                          reinterpret_cast<const unsigned char*>(data.data()), data.size()) != 1)
    {
        EVP_CIPHER_CTX_free(ctx);
        return false;
    }
    EVP_CIPHER_CTX_free(ctx);
    OPENSSL_cleanse(key, sizeof(key));
    ciphertext.resize(ciphertextLen);
    data = std::move(ciphertext);
    return true;
}



function weakDecrypt (data, password)
{
    if (!data.length)
        return false

    // Generate a salt from the password
    const pwhash = crypto.createHash('sha256').update(password).digest()
    const salt = Buffer.from(pwhash)

    const NONCE_SIZE = 12
    const KEY_SIZE = 32

    // Derive key from password
    const key = crypto.createHash('sha256').update(password).digest()

    // Derive nonce
    const context = Buffer.concat([Buffer.from(password), salt])
    const hash = crypto.createHash('sha256').update(context).digest()
    const nonce = hash.slice(0, NONCE_SIZE)

    // Log values for comparison
    console.log('\nkey\n', key.toString('base64'))
    console.log('\nnonce\n', nonce.toString('base64'))
    console.log('\nsalt\n', salt.toString('base64'))
    console.log('\ncontext\n', context.toString('base64'))

    try {
        const decipher = new JSChaCha20(key, nonce)
        let decrypted = decipher.decrypt(Buffer.from(data))
        decrypted = Buffer.from(decrypted).toString('utf8')
        return decrypted
    } catch (error) {
        console.error('Decryption failed:', error.message)
        return false
    }
}






'use strict'
/*
 * Copyright (c) 2017, Bubelich Mykola
 * https://www.bubelich.com
 *
 * ChaCha20 is a stream cipher designed by D. J. Bernstein.
 * It is a refinement of the Salsa20 algorithm, and it uses a 256-bit key.
 *
 * ChaCha20 successively calls the ChaCha20 block function, with the same key and nonce, and with successively increasing block counter parameters.
 * ChaCha20 then serializes the resulting state by writing the numbers in little-endian order, creating a keystream block.
 *
 * Concatenating the keystream blocks from the successive blocks forms a keystream.
 * The ChaCha20 function then performs an XOR of this keystream with the plaintext.
 * Alternatively, each keystream block can be XORed with a plaintext block before proceeding to create the next block, saving some memory.
 * There is no requirement for the plaintext to be an integral multiple of 512 bits.  If there is extra keystream from the last block, it is discarded.
 *
 * The inputs to ChaCha20 are
 * - 256-bit key
 * - 32-bit initial counter
 * - 96-bit nonce.  In some protocols, this is known as the Initialization Vector
 * - Arbitrary-length plaintext
 *
 * Implementation derived from chacha-ref.c version 20080118
 * See for details, 0x http, 0x//cr.yp.to/chacha/chacha-20080128.pdf
 */

/**
 *
 * @param {Uint8Array} key
 * @param {Uint8Array} nonce
 * @param {number} counter
 * @throws {Error}
 *
 * @constructor
 */
var JSChaCha20 = function(key, nonce, counter)
{
    if (typeof counter === 'undefined')
    {
        counter = 0
    }

    if (!(key instanceof Uint8Array) || key.length !== 32)
    {
        throw new Error('Key should be 32 byte array!')
    }

    if (!(nonce instanceof Uint8Array) || nonce.length !== 12)
    {
        throw new Error('Nonce should be 12 byte array!')
    }

    this._rounds = 20
    // Constants
    this._sigma = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]

    // param construction
    this._param = [
        this._sigma[0],
        this._sigma[1],
        this._sigma[2],
        this._sigma[3],
        // key
        this._get32(key, 0),
        this._get32(key, 4),
        this._get32(key, 8),
        this._get32(key, 12),
        this._get32(key, 16),
        this._get32(key, 20),
        this._get32(key, 24),
        this._get32(key, 28),
        // counter
        counter,
        // nonce
        this._get32(nonce, 0),
        this._get32(nonce, 4),
        this._get32(nonce, 8)
    ]

    // init 64 byte keystream block //
    this._keystream = [
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    ]

    // internal byte counter //
    this._byteCounter = 0
}

JSChaCha20.prototype._chacha = function()
{
    var mix = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    var i = 0
    var b = 0

    // copy param array to mix //
    for (i = 0; i < 16; i++)
    {
        mix[i] = this._param[i]
    }

    // mix rounds //
    for (i = 0; i < this._rounds; i += 2)
    {
        this._quarterround(mix, 0, 4, 8, 12)
        this._quarterround(mix, 1, 5, 9, 13)
        this._quarterround(mix, 2, 6, 10, 14)
        this._quarterround(mix, 3, 7, 11, 15)

        this._quarterround(mix, 0, 5, 10, 15)
        this._quarterround(mix, 1, 6, 11, 12)
        this._quarterround(mix, 2, 7, 8, 13)
        this._quarterround(mix, 3, 4, 9, 14)
    }

    for (i = 0; i < 16; i++)
    {
        // add
        mix[i] += this._param[i]

        // store keystream
        this._keystream[b++] = mix[i] & 0xFF
        this._keystream[b++] = (mix[i] >>> 8) & 0xFF
        this._keystream[b++] = (mix[i] >>> 16) & 0xFF
        this._keystream[b++] = (mix[i] >>> 24) & 0xFF
    }
}

/**
 * The basic operation of the ChaCha algorithm is the quarter round.
 * It operates on four 32-bit unsigned integers, denoted a, b, c, and d.
 *
 * @param {Array} output
 * @param {number} a
 * @param {number} b
 * @param {number} c
 * @param {number} d
 * @private
 */
JSChaCha20.prototype._quarterround = function(output, a, b, c, d)
{
    output[d] = this._rotl(output[d] ^ (output[a] += output[b]), 16)
    output[b] = this._rotl(output[b] ^ (output[c] += output[d]), 12)
    output[d] = this._rotl(output[d] ^ (output[a] += output[b]), 8)
    output[b] = this._rotl(output[b] ^ (output[c] += output[d]), 7)

    // JavaScript hack to make UINT32 :) //
    output[a] >>>= 0
    output[b] >>>= 0
    output[c] >>>= 0
    output[d] >>>= 0
}

/**
 * Little-endian to uint 32 bytes
 *
 * @param {Uint8Array|[number]} data
 * @param {number} index
 * @return {number}
 * @private
 */
JSChaCha20.prototype._get32 = function(data, index)
{
    return data[index++] ^ (data[index++] << 8) ^ (data[index++] << 16) ^ (data[index] << 24)
}

/**
 * Cyclic left rotation
 *
 * @param {number} data
 * @param {number} shift
 * @return {number}
 * @private
 */
JSChaCha20.prototype._rotl = function(data, shift)
{
    return ((data << shift) | (data >>> (32 - shift)))
}

/**
 *  Encrypt data with key and nonce
 *
 * @param {Uint8Array} data
 * @return {Uint8Array}
 */
JSChaCha20.prototype.encrypt = function(data)
{
    return this._update(data)
}

/**
 *  Decrypt data with key and nonce
 *
 * @param {Uint8Array} data
 * @return {Uint8Array}
 */
JSChaCha20.prototype.decrypt = function(data)
{
    return this._update(data)
}

/**
 *  Encrypt or Decrypt data with key and nonce
 *
 * @param {Uint8Array} data
 * @return {Uint8Array}
 * @private
 */
JSChaCha20.prototype._update = function(data)
{
    if (!(data instanceof Uint8Array) || data.length === 0)
    {
        throw new Error('Data should be type of bytes (Uint8Array) and not empty!')
    }

    var output = new Uint8Array(data.length)

    // core function, build block and xor with input data //
    for (var i = 0; i < data.length; i++)
    {
        if (this._byteCounter === 0 || this._byteCounter === 64)
        {
            // generate new block //

            this._chacha()
            // counter increment //
            this._param[12]++

            // reset internal counter //
            this._byteCounter = 0
        }

        output[i] = data[i] ^ this._keystream[this._byteCounter++]
    }

    return output
}

// EXPORT //
export default JSChaCha20