/* * Copyright 2018 Scytl Secure Electronic Voting SA * * All rights reserved * * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package */ cryptolib.modules.symmetric = cryptolib.modules.symmetric || {}; /** * Defines the symmetric ciphers. * * @namespace symmetric/cipher */ cryptolib.modules.symmetric.cipher = function(box) { 'use strict'; box.symmetric = box.symmetric || {}; box.symmetric.cipher = {}; /** * Allows a symmetric cipher factory to be obtained. * * @exports symmetric/cipher/factory */ box.symmetric.cipher.factory = {}; var policies = { cipher: { algorithm: box.policies.symmetric.cipher.algorithm, provider: box.policies.symmetric.cipher.provider, initializationVectorLengthBytes: box.policies.symmetric.cipher.initializationVectorLengthBytes } }; for (var prop in policies.cipher.algorithm) { if (typeof prop !== 'undefined') { policies.cipher.algorithm = policies.cipher.algorithm[prop]; } } var utils, exceptions, randomFactory; var f = function(box) { utils = box.commons.utils; exceptions = box.commons.exceptions; randomFactory = new box.primitives.securerandom.factory.SecureRandomFactory(); }; f.policies = { primitives: { secureRandom: {provider: box.policies.symmetric.cipher.secureRandom.provider} } }; cryptolib('commons', 'primitives.securerandom', f); /** * Factory class that allows a symmetric cipher to be obtained. * * @class */ box.symmetric.cipher.factory.SymmetricCipherFactory = function() {}; box.symmetric.cipher.factory.SymmetricCipherFactory.prototype = { /** * Create a new symmetric cipher. *

* The particular cipher that is returned depends on the policies. * * @function * @returns a new symmetric cipher. */ getCryptoSymmetricCipher: function() { var secureRandom; try { if (policies.cipher.provider === Config.symmetric.cipher.provider.FORGE) { secureRandom = randomFactory.getCryptoRandomBytes(); return this.getCryptoForgeSymmetricCipher(secureRandom); } else { throw new exceptions.CryptoLibException( 'No suitable provider was provided.'); } } catch (error) { throw new exceptions.CryptoLibException( 'A CryptoSymmetricCipher could not be obtained.', error); } }, /** * Create a new CryptoForgeSymmetricCipher. * * @function * @param secureRandom * {CryptoScytlRandomBytes} a secure source of random bytes. * @returns {symmetric/cipher.CryptoForgeSymmetricCipher} a new * CryptoForgeSymmetricCipher. */ getCryptoForgeSymmetricCipher: function(secureRandom) { return new CryptoForgeSymmetricCipher(secureRandom); } }; /** * @class * @param randomGenerator * {CryptoScytlRandomBytes} a secure random bytes generator. * @memberof symmetric/cipher */ function CryptoForgeSymmetricCipher(randomGenerator) { this.converters = new utils.Converters(); this.bitOperators = new utils.BitOperators(); this.randomGenerator = randomGenerator; } CryptoForgeSymmetricCipher.prototype = { /** * Encrypts some data. * * @function * @param data * {String} Data to be encrypted, as string in Base64 encoded * format. * @param keyB64 * {String} Key, as string in Base64 encoded format. * @returns Initialization vector concatenated with the encrypted data, * as string in Base64 encoded format. */ encrypt: function(keyB64, dataB64) { try { var key = this.converters.base64Decode(keyB64); var data = this.converters.base64Decode(dataB64); if (key.length !== policies.cipher.algorithm.keyLengthBytes) { throw new exceptions.CryptolibException( 'Key length does not match the required by the algorithm.'); } var initVec = this.randomGenerator.nextRandom( policies.cipher.initializationVectorLengthBytes); // Create a byte buffer for data. var dataBuffer = new box.forge.util.ByteBuffer(data); var cipher = box.forge.cipher.createCipher(policies.cipher.algorithm.name, key); // Only for the GCM mode var gcmAuthTagByteLength = policies.cipher.algorithm.tagLengthBytes; if (typeof gcmAuthTagBitLength !== 'undefined') { cipher.start({iv: initVec, tagLength: gcmAuthTagByteLength}); } else { cipher.start({iv: initVec}); } cipher.update(dataBuffer); cipher.finish(); var encryptedData = cipher.output.getBytes().toString(); if (typeof gcmAuthTagByteLength !== 'undefined') { var gcmAuthTag = cipher.mode.tag.getBytes(); encryptedData = encryptedData + gcmAuthTag.toString(); } var initVectorAndEncryptedData = initVec + encryptedData; var initVectorAndEncryptedDataBase64 = this.converters.base64Encode(initVectorAndEncryptedData); return initVectorAndEncryptedDataBase64; } catch (error) { throw new exceptions.CryptoLibException( 'Data could not be symmetrically encrypted.', error); } }, /** * Decrypts some encrypted data, using the initialization vector that is * provided * * @function * @param keyB64 * {String} the key as a string in Base64 encoded format. * @param initVectorAndEncryptedDataBase64 * {String} Initialization vector concatenated with the * encrypted data, as string in Base64 encoded format. * @returns Decrypted data, as string in Base64 encoded format. */ decrypt: function(keyB64, initVectorAndEncryptedDataBase64) { try { var key = this.converters.base64Decode(keyB64); if (key.length !== policies.cipher.algorithm.keyLengthBytes) { throw new exceptions.CryptolibException( 'Key length does not match the required by the algorithm.'); } var cipher = box.forge.cipher.createDecipher( policies.cipher.algorithm.name, key); var initVectorAndEncryptedData = this.converters.base64Decode(initVectorAndEncryptedDataBase64); var initVector = this.bitOperators.extract( initVectorAndEncryptedData, 0, policies.cipher.initializationVectorLengthBytes); var encryptedData = this.bitOperators.extract( initVectorAndEncryptedData, policies.cipher.initializationVectorLengthBytes, initVectorAndEncryptedData.length); // Only for the GCM mode var gcmAuthTagByteLength = policies.cipher.algorithm.tagLengthBytes; if (typeof gcmAuthTagByteLength !== 'undefined') { var offset = encryptedData.length - gcmAuthTagByteLength; var gcmAuthTag = this.bitOperators.extract( encryptedData, offset, encryptedData.length); encryptedData = this.bitOperators.extract(encryptedData, 0, offset); var gcmAuthTagBitLength = gcmAuthTagByteLength * box.NUMBER_OF_BITS_PER_BYTE; cipher.start({ iv: initVector, tagLength: gcmAuthTagBitLength, tag: gcmAuthTag }); } else { cipher.start({iv: initVector}); } var encryptedDataBuffer = new box.forge.util.ByteBuffer(encryptedData); cipher.update(encryptedDataBuffer); cipher.finish(); var outputBase64 = this.converters.base64Encode(cipher.output.getBytes().toString()); return outputBase64; } catch (error) { throw new exceptions.CryptoLibException( 'Data could not be symmetrically decrypted.', error); } } }; };