denoland / deploy_feedback

For reporting issues with Deno Deploy
https://deno.com/deploy
74 stars 5 forks source link

Deno deploy password hashing. #293

Open AshwinDeTaeye opened 1 year ago

AshwinDeTaeye commented 1 year ago

Is there a way to hash passwords async in deno deploy?

I've tried many modules like bcrypt, argon2, sodium & crypt. Most of them work in local deno environment. But none of them on deno deploy. Unless using a sync method.

For bcrypt there is an issue open: https://github.com/denoland/deploy_feedback/issues/171

Is there a standard non-blocking way of doing password hashing in deno? Maybe through the crypto web API.

https://bradyjoslin.com/blog/encryption-webcrypto/

https://doc.deno.land/deno/stable/~/SubtleCrypto

AshwinDeTaeye commented 1 year ago

Encrypt or decrypt passwords on deno deploy async:

const buff_to_base64 = (buff: number[] | Uint8Array) =>
  btoa(String.fromCharCode.apply(null, buff));

const base64_to_buf = (b64: string) =>
  Uint8Array.from(atob(b64), (c) => c.charCodeAt(null));

const enc = new TextEncoder();
const dec = new TextDecoder();

const getPasswordKey = async (password: any) =>
  await window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    "PBKDF2",
    false,
    ['deriveBits', "deriveKey"]
  );

const deriveKey = async (passwordKey: any, salt: any, keyUsage: any[]) =>
  await window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 250000,
      hash: "SHA-256",
    },
    passwordKey,
    { name: "AES-GCM", length: 256 },
    false,
    keyUsage
  );

export async function encryptData(secretData: string | undefined, password: any) {
  try {
    const salt = window.crypto.getRandomValues(new Uint8Array(16));
    const iv = window.crypto.getRandomValues(new Uint8Array(12));
    const passwordKey = await getPasswordKey(password);
    const aesKey = await deriveKey(passwordKey, salt, ["encrypt"]);
    const encryptedContent = await window.crypto.subtle.encrypt(
      {
        name: "AES-GCM",
        iv: iv,
      },
      aesKey,
      enc.encode(secretData)
    );

    const encryptedContentArr = new Uint8Array(encryptedContent);
    let buff = new Uint8Array(
      salt.byteLength + iv.byteLength + encryptedContentArr.byteLength
    );
    buff.set(salt, 0);
    buff.set(iv, salt.byteLength);
    buff.set(encryptedContentArr, salt.byteLength + iv.byteLength);
    const base64Buff = buff_to_base64(buff);
    return base64Buff;
  } catch (e) {
    console.log(`Error - ${e}`);
    return "";
  }
}

export async function decryptData(
  encryptedData: string,
  password: string | undefined
) {
  try {
    const encryptedDataBuff = base64_to_buf(encryptedData);
    const salt = encryptedDataBuff.slice(0, 16);
    const iv = encryptedDataBuff.slice(16, 16 + 12);
    const data = encryptedDataBuff.slice(16 + 12);
    const passwordKey = await getPasswordKey(password);
    const aesKey = await deriveKey(passwordKey, salt, ["decrypt"]);
    const decryptedContent = await window.crypto.subtle.decrypt(
      {
        name: "AES-GCM",
        iv: iv,
      },
      aesKey,
      data
    );
    return dec.decode(decryptedContent);
  } catch (e) {
    console.log(`Error - ${e}`);
    return "";
  }
}
jarrodu commented 1 year ago

I just ran into the same issue with bcrypt. I also, was trying to hash a password.

jarrodu commented 1 year ago

I used the synced versions of hash and compare. That worked on Deno Deploy.

juslin03 commented 7 months ago

I dont know why but, after trying with Sync, it works.

Thank @jarrodu