UNPKG

scriptremote

Version:

Message server providing secure remote access to scripted applications from a browser UI

228 lines (206 loc) 7.44 kB
'use strict' // // Service to encrypt/decrypt // angular.module('cryptoMod') .factory('CryptoSvc', ['$window', 'AlertSvc', function ($window, AlertSvc) { // Encryption is AES-256-CTR // // Two formats supported: // // 'openssl enc' format: 'Salted__' + 8-byte-salt + ciphertext, // in base64. Uses openssl custom key and iv generation. // // 'alternate' format: 12-byte-salt + ciphertext, in base64. // iv is salt + 4-byte-counter, initially zero. // Single encryption key per project. Key generation uses // pbkdf2 with sha256 hash, a separate 8-byte salt, // and 10000 rounds. // var service = {keySize: 32, ivSize: 16, passphrase: '', encrypted: false, keyGenIters: 10000, opensslPrefix: 'Salted__', aesKeys: {}}; service.init = function() { if ($window.localStorage.getItem('passphrase') && ($window.localStorage.getItem('passphrase') !== '')) { service.passphrase = $window.localStorage.getItem('passphrase'); service.encrypted = true; } else if ($window.sessionStorage.getItem('passphrase') && ($window.sessionStorage.getItem('passphrase') !== '')) { service.passphrase = $window.sessionStorage.getItem('passphrase'); service.encrypted = true; } else { service.passphrase = ''; service.encrypted = false; service.aesKeys = {}; } } // Compute project aes encryption key service.computeAESKey = function(project, salt) { // Projects using openssl format have empty project salt if (salt.length > 0) { var data64 = salt.replace(/-/g,'/'); var binary = forge.util.decode64(data64); var buf = forge.util.createBuffer(binary, 'raw'); salt = buf.getBytes(8) var key = forge.pkcs5.pbkdf2(service.passphrase, salt, service.keyGenIters, service.keySize, 'sha256'); service.aesKeys[project] = key; } } // Decrypt a data item after ajax service.decryptBase = function(project, data, isBinary) { if (service.passphrase === '') { AlertSvc.msgAlert("error", "No passphrase found"); return data; } // Using modified base64 to simplify url handling var data64 = data.replace(/-/g,'/'); var binary = forge.util.decode64(data64); var input = forge.util.createBuffer(binary, 'raw'); // Get key and iv var iv; var key; var salt; var buffer; if (project && (project in service.aesKeys)) { salt = input.getBytes(12); buffer = forge.util.createBuffer(salt); for (var i = 0; i < 4; i++) { buffer.putByte(0); } iv = buffer.getBytes(service.ivSize); key = service.aesKeys[project]; } else { var prefix = input.getBytes(8); if (prefix != service.opensslPrefix) { AlertSvc.msgAlert("error", "Data not in openssl format"); return null; } salt = input.getBytes(8); var derivedBytes = forge.pbe.opensslDeriveBytes(service.passphrase, salt, service.keySize + service.ivSize); buffer = forge.util.createBuffer(derivedBytes); key = buffer.getBytes(service.keySize); iv = buffer.getBytes(service.ivSize); } //var salt16 = forge.util.bytesToHex(salt); //var key16 = forge.util.bytesToHex(key); //var iv16 = forge.util.bytesToHex(iv); // Decrypt var decipher = forge.cipher.createDecipher('AES-CTR', key); decipher.start({iv: iv}); decipher.update(input); var result = decipher.finish(); var output = ''; if (result) { if (isBinary) { output = decipher.output.getBytes(); } else { output = decipher.output.toString(); } } else { AlertSvc.msgAlert("error", "Decryption failed"); } return output; } // Decrypt a data item with error catch service.decrypt = function(project, data, isBinary) { if (service.passphrase === '') { AlertSvc.msgAlert("error", "No passphrase found"); return data; } var plain = null; try { plain = service.decryptBase(project, data, isBinary); } catch(e) { return null; } if (!plain || (plain.length === 0)) { return null; } return plain; } // Encrypt a data item before ajax service.encryptBase = function(project, data) { if (service.passphrase === '') { AlertSvc.msgAlert("error", "No passphrase found"); return data; } // Generate key and iv var key; var iv; var salt; var buffer; var openssl; if (project && (project in service.aesKeys)) { salt = forge.random.getBytesSync(12); buffer = forge.util.createBuffer(salt); for (var i = 0; i < 4; i++) { buffer.putByte(0); } iv = buffer.getBytes(service.ivSize); key = service.aesKeys[project]; openssl = false; } else { salt = forge.random.getBytesSync(8); var derivedBytes = forge.pbe.opensslDeriveBytes(service.passphrase, salt, service.keySize + service.ivSize); buffer = forge.util.createBuffer(derivedBytes); key = buffer.getBytes(service.keySize); iv = buffer.getBytes(service.ivSize); openssl = true; } // Encrypt var input = forge.util.createBuffer(data, 'binary'); var cipher = forge.cipher.createCipher('AES-CTR', key); cipher.start({iv: iv}); cipher.update(input); cipher.finish(); var ciphertext = cipher.output.getBytes(); buffer = forge.util.createBuffer(); if (openssl) { buffer.putBytes(service.opensslPrefix); } buffer.putBytes(salt); buffer.putBytes(ciphertext); var data64 = forge.util.encode64(buffer.getBytes()); // Using modified base64 to simplify url handling var output = data64.replace(/\//g,'-'); return output; } // Encrypt a data item with error catch service.encrypt = function(project, data) { var cipher; if (service.passphrase === '') { AlertSvc.msgAlert("error", "No passphrase found"); return data; } var plain = null; try { cipher = service.encryptBase(project, data); } catch(e) { return null; } if (!cipher || (cipher.length === 0)) { return null; } return cipher; } service.hmac = function(data) { var hmac1 = forge.hmac.create(); hmac1.start('sha256', service.passphrase); hmac1.update(data); return hmac1.digest().toHex(); } service.isEncrypted = function() { return service.encrypted; } service.toBase64 = function(data) { return forge.util.encode64(data); } service.init(); return service; }])