yael.js 7.54 KB
// Generated by CoffeeScript 1.10.0
(function() {
  var CipherObject, E, crypto, decrypt, decryptAsync, decryptPromise, decryptSync, encrypt, encryptAsync, encryptPromise, encryptSync, incompatibleVersion, pkg, semver;

  crypto = require('crypto');

  semver = require('semver');

  pkg = require('./package.json');

  CipherObject = require('./CipherObject');

  E = require('./constants');

  encrypt = function(passphrase, plainfile, callback) {
    switch (arguments.length) {
      case 1:
        return encryptStream(passphrase);
      case 2:
        return encryptPromise(passphrase, plainfile);
      case 3:
        return encryptAsync(passphrase, plainfile, callback);
    }
  };

  decrypt = function(passphrase, cipherObject, callback) {
    switch (arguments.length) {
      case 1:
        return decryptStream(passphrase);
      case 2:
        return decryptPromise(passphrase, cipherObject);
      case 3:
        return decryptAsync(passphrase, cipherObject, callback);
    }
  };

  encryptAsync = function(passphrase, plainfile, callback) {
    var salt;
    salt = crypto.randomBytes(E.SALT_LENGTH);
    crypto.pbkdf2(passphrase, salt, E.ITERATIONS, E.KEY_LENGTH, E.HASH_ALGORITHM, function(err, key) {
      var cipher, iv;
      if (err != null) {
        return callback(err);
      }
      iv = crypto.randomBytes(E.IV_LENGTH);
      cipher = crypto.createCipheriv(E.CIPHER_ALGORITHM, key, iv);
      return cipher.end(plainfile, function(err) {
        var authtag, cipherfile, result;
        if (err != null) {
          return callback(err);
        }
        cipherfile = cipher.read();
        authtag = cipher.getAuthTag();
        result = new CipherObject({
          yael_version: pkg.version,
          cipherfile: cipherfile,
          iv: iv,
          salt: salt,
          authtag: authtag,
          return_type: (function() {
            switch (false) {
              case typeof plainfile !== 'string':
                return 'String';
              case !(plainfile instanceof Buffer):
                return 'Buffer';
              default:
                return 'undefined';
            }
          })(),
          details: {
            CIPHER_ALGORITHM: E.CIPHER_ALGORITHM,
            SALT_LENGTH: E.SALT_LENGTH,
            IV_LENGTH: E.IV_LENGTH,
            KEY_LENGTH: E.KEY_LENGTH,
            HASH_ALGORITHM: E.HASH_ALGORITHM,
            ITERATIONS: E.ITERATIONS
          }
        });
        return callback(null, result);
      });
    });
    return null;
  };

  decryptAsync = function(passphrase, cipherObject, callback) {
    var authtag, cipherfile, err, iv, return_type, salt, yael_version;
    yael_version = cipherObject.yael_version, cipherfile = cipherObject.cipherfile, iv = cipherObject.iv, salt = cipherObject.salt, authtag = cipherObject.authtag, return_type = cipherObject.return_type;
    err = incompatibleVersion(yael_version);
    if (err != null) {
      return callback(err);
    }
    crypto.pbkdf2(passphrase, salt, E.ITERATIONS, E.KEY_LENGTH, E.HASH_ALGORITHM, function(err, key) {
      var decipher, error;
      if (err != null) {
        return callback(err);
      }
      decipher = crypto.createDecipheriv(E.CIPHER_ALGORITHM, key, iv);
      decipher.setAuthTag(authtag);
      try {
        return decipher.end(cipherfile, function(err) {
          var plainfile;
          if (err != null) {
            return callback(err);
          }
          plainfile = decipher.read();
          if (return_type === 'String') {
            plainfile = plainfile.toString();
          }
          return callback(null, plainfile);
        });
      } catch (error) {
        err = error;
        if (err.message === "Unsupported state or unable to authenticate data") {
          return callback(new Error("Message Corrupted"));
        }
        if (err != null) {
          return callback(err);
        }
      }
    });
    return null;
  };

  encryptPromise = function(passphrase, plaintext) {
    return new Promise(function(resolve, reject) {
      return encryptAsync(passphrase, plaintext, function(err, result) {
        if (err != null) {
          return reject(err);
        }
        return resolve(result);
      });
    });
  };

  decryptPromise = function(passphrase, cipherObject) {
    return new Promise(function(resolve, reject) {
      return decryptAsync(passphrase, cipherObject, function(err, result) {
        if (err != null) {
          return reject(err);
        }
        return resolve(result);
      });
    });
  };

  encryptSync = function(passphrase, plainfile) {
    var authtag, cipher, cipherfile, iv, key, result, return_type, salt;
    return_type = (function() {
      switch (false) {
        case typeof plainfile !== 'string':
          return 'String';
        case !(plainfile instanceof Buffer):
          return 'Buffer';
        default:
          return 'undefined';
      }
    })();
    if (return_type === 'String') {
      plainfile = new Buffer(plainfile, 'utf8');
    }
    salt = crypto.randomBytes(E.SALT_LENGTH);
    key = crypto.pbkdf2Sync(passphrase, salt, E.ITERATIONS, E.KEY_LENGTH, E.HASH_ALGORITHM);
    iv = crypto.randomBytes(E.IV_LENGTH);
    cipher = crypto.createCipheriv(E.CIPHER_ALGORITHM, key, iv);
    cipherfile = Buffer.concat([cipher.update(plainfile), cipher.final()]);
    authtag = cipher.getAuthTag();
    result = new CipherObject({
      yael_version: pkg.version,
      cipherfile: cipherfile,
      iv: iv,
      salt: salt,
      authtag: authtag,
      return_type: return_type,
      details: {
        CIPHER_ALGORITHM: E.CIPHER_ALGORITHM,
        SALT_LENGTH: E.SALT_LENGTH,
        IV_LENGTH: E.IV_LENGTH,
        KEY_LENGTH: E.KEY_LENGTH,
        HASH_ALGORITHM: E.HASH_ALGORITHM,
        ITERATIONS: E.ITERATIONS
      }
    });
    return result;
  };

  decryptSync = function(passphrase, cipherObject) {
    var authtag, cipherfile, decipher, err, error, iv, key, plainfile, return_type, salt, yael_version;
    yael_version = cipherObject.yael_version, cipherfile = cipherObject.cipherfile, iv = cipherObject.iv, salt = cipherObject.salt, authtag = cipherObject.authtag, return_type = cipherObject.return_type;
    err = incompatibleVersion(yael_version);
    if (err != null) {
      throw err;
    }
    key = crypto.pbkdf2Sync(passphrase, salt, E.ITERATIONS, E.KEY_LENGTH, E.HASH_ALGORITHM);
    decipher = crypto.createDecipheriv(E.CIPHER_ALGORITHM, key, iv);
    decipher.setAuthTag(authtag);
    try {
      plainfile = Buffer.concat([decipher.update(cipherfile), decipher.final()]);
      if (return_type === 'String') {
        plainfile = plainfile.toString('utf8');
      }
      return plainfile;
    } catch (error) {
      err = error;
      if (err.message === "Unsupported state or unable to authenticate data") {
        throw new Error("Message Corrupted");
      }
    }
  };

  incompatibleVersion = function(yael_version) {
    if (semver.valid(yael_version) == null) {
      return new Error("cipherObject cannot be read because cipherObject.yael_version is not a valid semver");
    }
    if (semver.major(yael_version) !== semver.major(pkg.version)) {
      return new Error("cipherObject cannot be read because cipherObject.yael_version is incompatible with this version of yael");
    }
    if (semver.minor(yael_version) > semver.minor(pkg.version)) {
      return new Error("cipherObject cannot be read because cipherObject.yael_version indicates the cipherObject was made by a newer version of yael");
    }
    return null;
  };

  module.exports = {
    encrypt: encrypt,
    encryptSync: encryptSync,
    decrypt: decrypt,
    decryptSync: decryptSync,
    CipherObject: CipherObject
  };

}).call(this);