Repository: mozilla/send
File: frontend/src/fileSender.js (JavaScript)

Code before:
      };

      xhr.send(JSON.stringify({ delete_token: token }));
    });
  }

  cancel() {
    this.uploadXHR.abort();
  }

  upload() {
    const self = this;
    self.emit('loading', true);
    return Promise.all([
      window.crypto.subtle
        .generateKey(
          {
            name: 'AES-GCM',
            length: 128
          },
          true,
          ['encrypt', 'decrypt']
        )
        .catch(err =>
          // eslint-disable-next-line no-console
          console.error('There was an error generating a crypto key')
        ),
      new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsArrayBuffer(this.file);
        reader.onload = function(event) {
          self.emit('loading', false);
          self.emit('hashing', true);
          const plaintext = new Uint8Array(this.result);
          window.crypto.subtle.digest('SHA-256', plaintext).then(hash => {
            self.emit('hashing', false);
            self.emit('encrypting', true);
            resolve({ plaintext: plaintext, hash: new Uint8Array(hash) });
          });
        };
        reader.onerror = function(err) {
          reject(err);
        };
      })
    ])
      .then(([secretKey, file]) => {
        return Promise.all([
          window.crypto.subtle
            .encrypt(
              {
                name: 'AES-GCM',

Review comment (bug):
Kept this since removing the `catch()` would alter the code paths. This `catch()` is actually swallowing the error, IIUC. So if `window.crypto.subtle.generateKey()` fails for whatever reason, we're intercepting the error and just ignoring it, allowing `Promise.all()` to resolve with potentially an `undefined` or `null` `secretKey`. 

Or not, maybe I'm wrong. 🤷‍♀️

Code after:
      };

      xhr.send(JSON.stringify({ delete_token: token }));
    });
  }

  cancel() {
    this.uploadXHR.abort();
  }

  upload() {
    const self = this;
    self.emit('loading', true);
    return Promise.all([
      window.crypto.subtle
        .generateKey(
          {
            name: 'AES-GCM',
            length: 128
          },
          true,
          ['encrypt', 'decrypt']
        ),
      new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsArrayBuffer(this.file);
        reader.onload = function(event) {
          self.emit('loading', false);
          self.emit('hashing', true);
          const plaintext = new Uint8Array(this.result);
          window.crypto.subtle.digest('SHA-256', plaintext).then(hash => {
            self.emit('hashing', false);
            self.emit('encrypting', true);
            resolve({ plaintext: plaintext, hash: new Uint8Array(hash) });
          });
        };
        reader.onerror = function(err) {
          reject(err);
        };
      })
    ])
      .then(([secretKey, file]) => {
        return Promise.all([
          window.crypto.subtle
            .encrypt(
              {
                name: 'AES-GCM',
                iv: this.iv,
                additionalData: file.hash,
                tagLength: 128
              },