Search code examples
javascriptreactjscryptographyaes-gcm

Encrypt File from browser AES-GCM


I have a problem with encrypting and decrypting files with AES GCM, I encrypt/decrypt my files with this code:

I tried many some libraries but they didnt solve my problem.

var FileSaver = require("file-saver");

export function formatBytes(bytes) {
  var marker = 1024; // Change to 1000 if required
  var decimal = 3; // Change as required
  var kiloBytes = marker; // One Kilobyte is 1024 bytes
  var megaBytes = marker * marker; // One MB is 1024 KB
  var gigaBytes = marker * marker * marker; // One GB is 1024 MB

  // return bytes if less than a KB
  if (bytes < kiloBytes) return bytes + " Bytes";
  // return KB if less than a MB
  else if (bytes < megaBytes)
    return (bytes / kiloBytes).toFixed(decimal) + " KB";
  // return MB if less than a GB
  else if (bytes < gigaBytes)
    return (bytes / megaBytes).toFixed(decimal) + " MB";
  // return GB if less than a TB
  else return (bytes / gigaBytes).toFixed(decimal) + " GB";
}

export const encryptFile = async (key, iv, file) => {
  return await window.crypto.subtle.encrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    key,
    file
  );
};

/*
  Get some key material to use as input to the deriveKey method.
  The key material is a password supplied by the user.
  */
export const getKeyMaterial = async (password) => {
  let enc = new TextEncoder();
  return window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits", "deriveKey"]
  );
};

/*
Given some key material and some random salt
derive an AES-GCM key using PBKDF2.
*/
export const getKey = async (keyMaterial, salt) => {
  return window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 100000,
      hash: "SHA-256"
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
};

// get the iv which is similar the salt value used for encryption
export const getiv = () => {
  return window.crypto.getRandomValues(new Uint8Array(12));
};

// load the file in the memory
export const getFile = async (inputFile) => {
  return await inputFile.arrayBuffer();
};

export const decryptFile = (iv, key, cipherText) => {
  return window.crypto.subtle.decrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    key,
    cipherText
  );
};

export const getDigest = (uid) => {
  let enc = new TextEncoder();

  return crypto.subtle.digest("SHA-256", enc.encode(uid));
};

export const startEncryption = async (file, password) => {
  console.log(file, password);
  let digest = getDigest(password);
  let rawFile = getFile(file);
  let keyMaterial = getKeyMaterial();
  Promise.all([digest, rawFile, keyMaterial]).then((values) => {
    console.log(values);
    let salt = new Uint8Array(
      "12345678".match(/[\da-f]{2}/gi).map(function (h) {
        return parseInt(h, 16);
      })
    );

    let iv = new Uint8Array(
      "4142434445464748494a4b4c4d4e4f50"
        .match(/[\da-f]{2}/gi)
        .map(function (h) {
          return parseInt(h, 16);
        })
    );
    // generate a crypto key
    getKey(values[2], salt).then((resp) => {
      console.log(resp);
      encryptFile(resp, iv, values[1]).then((cipherText) => {
        let fileBlob = new Blob([cipherText], { type: file.type });
        FileSaver.saveAs(fileBlob, file.name);
      });
    });
  });
};

export const startDecryption = (cipherText, password) => {
  let keyMaterial = getKeyMaterial();
  let digest = getDigest(password);
  let rawFile = getFile(cipherText);
  Promise.all([digest, rawFile, keyMaterial]).then((values) => {
    let salt = new Uint8Array(
      "12345678".match(/[\da-f]{2}/gi).map(function (h) {
        return parseInt(h, 16);
      })
    );

    let iv = new Uint8Array(
      "4142434445464748494a4b4c4d4e4f50"
        .match(/[\da-f]{2}/gi)
        .map(function (h) {
          return parseInt(h, 16);
        })
    );

    // generate a crypto key
    getKey(values[2], salt).then((resp) => {
      decryptFile(iv, resp, values[1]).then((file) => {
        let fileBlob = new Blob([file], { type: file.type });
        FileSaver.saveAs(fileBlob, file.name);
      });
    });
  });
};

Files can be encrypt/decrypted but they can be decrypted with any password even if it doesnt match the password it encrypted with. What am i missing here?


Solution

  • you call getKeyMaterial() without the password, so its always undefined. try getKeyMaterial(password);