jedisct1 / libsodium.js

libsodium compiled to Webassembly and pure JavaScript, with convenient wrappers.
Other
968 stars 138 forks source link

Help me implementing aead_chacha20 #302

Closed mohitxskull closed 2 years ago

mohitxskull commented 2 years ago
import _sodium from 'libsodium-wrappers';

const ChaCha20 = {
  Enc: async (PASSWORD: string, INPUT: string) => {
    await _sodium.ready;
    const sodium = _sodium;

    const salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
    const nonce = sodium.randombytes_buf(8, 'uint8array');
    const key = sodium.crypto_pwhash(
      sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,
      PASSWORD,
      salt,
      sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
      sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
      sodium.crypto_pwhash_ALG_ARGON2ID13
    );

    const EncRes = sodium.crypto_aead_chacha20poly1305_encrypt(
      INPUT,
      null,
      null,
      nonce,
      key,
      'base64'
    );

    return { Res: EncRes, Nonce: nonce, salt };
  },

  Dec: async (
    PASSWORD: string,
    INPUT: string,
    NONCE: Uint8Array,
    SALT: Uint8Array
  ) => {
    await _sodium.ready;
    const sodium = _sodium;

    const key = sodium.crypto_pwhash(
      sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES,
      PASSWORD,
      SALT,
      sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
      sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
      sodium.crypto_pwhash_ALG_ARGON2ID13
    );

    const DecRes = sodium.crypto_aead_chacha20poly1305_decrypt(
      null,
      INPUT,
      null,
      NONCE,
      key,
      'base64'
    );

    return DecRes;
  },
};

export default ChaCha20;

Encryption is working!

Enc => 
Object { Res: "036oC4KsUM9NG6jr0X324q92DV2H", Nonce: Uint8Array(8), salt: Uint8Array(16) }
​
Nonce: Uint8Array(8) [ 3, 137, 177, … ]
​
Res: "036oC4KsUM9NG6jr0X324q92DV2H"
​
salt: Uint8Array(16) [ 216, 230, 35, … ]

But when i try to decrypt my data, it throw's an error!

Uncaught (in promise) Error: ciphertext cannot be decrypted using that key
    g libsodium-wrappers.js:1
    E libsodium-wrappers.js:1
    _callee$ ChaCha20.ts:49
    Babel 10
    _callee$ dev.tsx:14
    Babel 10
    React 22
libsodium-wrappers.js:1:17794
jedisct1 commented 2 years ago

sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES

Since you're using aead_chacha20poly1305, that should be crypto_aead_chacha20poly1305_KEYBYTES

nonce = sodium.randombytes_buf(8, 'uint8array');

The nonce size should be crypto_aead_chacha20poly1305_NONCEBYTES. I don't think 8 is correct.

sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
sodium.crypto_pwhash_ALG_ARGON2ID13

You're mixing things as well. pwhash_OPSLIMIT_ (and _MEMLIMIT) are for the default algorithm so the algorithm should be sodium.crypto_pwhash_ALG_DEFAULT.

jedisct1 commented 2 years ago
const DecRes = sodium.crypto_aead_chacha20poly1305_decrypt(
      null,
      INPUT,
      null,
      NONCE,
      key,
      'base64'
    );

base64 is how the output of the function call will be encoded, not the input.

So INPUT is still base64 encoded here.

Change it to:

    const DecRes = sodium.crypto_aead_chacha20poly1305_decrypt(
      null,
      sodium.from_base64(INPUT),
      null,
      NONCE,
      key
    );
mohitxskull commented 2 years ago

Thank you so much sir!!

This is how I am using is right now and it's working great!

import _sodium from 'libsodium-wrappers';

const ChaCha20 = {
  Enc: async (KEY: string, INPUT: string) => {
    await _sodium.ready;
    const Sodium = _sodium;

    const Nonce = Sodium.randombytes_buf(
      Sodium.crypto_aead_chacha20poly1305_NPUBBYTES
    );

    const EncRes = Sodium.crypto_aead_chacha20poly1305_encrypt(
      INPUT,
      null,
      null,
      Nonce,
      Sodium.from_base64(KEY),
      'base64'
    );

    return `${EncRes}$${Sodium.to_base64(Nonce)}`;
  },
  Dec: async (KEY: string, INPUT: string) => {
    await _sodium.ready;
    const Sodium = _sodium;

    const Nonce = INPUT.split('$')[1];
    const Cipher = INPUT.split('$')[0];

    const DecRes = Sodium.crypto_aead_chacha20poly1305_decrypt(
      null,
      Sodium.from_base64(Cipher),
      null,
      Sodium.from_base64(Nonce),
      Sodium.from_base64(KEY),
      'text'
    );

    return DecRes;
  },
};

export default ChaCha20;
jedisct1 commented 2 years ago

Glad to hear that it works!