/* * 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 */ /* jshint node:true, jasmine:true */ 'use strict'; var cryptoPolicy = require('scytl-cryptopolicy'); var symmetric = require('../lib/index'); var secureRandom = require('scytl-securerandom'); var bitwise = require('scytl-bitwise'); var codec = require('scytl-codec'); describe('The symmetric cryptography module should be able to ...', function() { var DATA = 'anystring'; var OTHER_DATA_1 = DATA + 'a'; var OTHER_DATA_2 = DATA + 'b'; var data_; var otherData1_; var otherData2_; var dataParts_; var service_; var policy_ = cryptoPolicy.newInstance(); var keyGenerator_; var encryptionKey_; var macKey_; var cipher_; var macHandler_; var encryptedData_; var mac_; beforeAll(function() { data_ = codec.utf8Encode(DATA); otherData1_ = codec.utf8Encode(OTHER_DATA_1); otherData2_ = codec.utf8Encode(OTHER_DATA_2); dataParts_ = [data_, otherData1_, otherData2_]; service_ = symmetric.newService(); policy_ = cryptoPolicy.newInstance(); keyGenerator_ = service_.newKeyGenerator(); encryptionKey_ = keyGenerator_.nextEncryptionKey(); macKey_ = keyGenerator_.nextMacKey(); cipher_ = service_.newCipher().init(encryptionKey_); macHandler_ = service_.newMacHandler().init(macKey_); encryptedData_ = cipher_.encrypt(data_); mac_ = macHandler_.generate(data_); }); describe('create a symmetric cryptography service that should be able to ..', function() { describe('create a secret key generator that should be able to', function() { it('generate a secret key for encryption', function() { expect(encryptionKey_.length) .toBe(policy_.symmetric.secretKey.encryption.lengthBytes); }); it('generate a secret key for mac generation', function() { expect(macKey_.length) .toBe(policy_.symmetric.secretKey.mac.lengthBytes); }); it('generate secret keys, using a specified cryptographic policy', function() { var policy = cryptoPolicy.newInstance(); policy.symmetric.secretKey.encryption.lengthBytes = cryptoPolicy.options.symmetric.secretKey.encryption.lengthBytes .KL_32; policy.symmetric.secretKey.mac.lengthBytes = cryptoPolicy.options.symmetric.secretKey.mac.lengthBytes.KL_64; var service = symmetric.newService({policy: policy}); var keyGenerator = service.newKeyGenerator(); var encryptionKey = keyGenerator.nextEncryptionKey(); expect(encryptionKey.length) .toBe(policy.symmetric.secretKey.encryption.lengthBytes); var macKey = keyGenerator.nextMacKey(); expect(macKey.length) .toEqual(policy.symmetric.secretKey.mac.lengthBytes); }); it('generate secret keys, using a specified secure random service object', function() { var secureRandomService = secureRandom.newService(); var service = symmetric.newService({secureRandomService: secureRandomService}); var keyGenerator = service.newKeyGenerator(); var encryptionKey = keyGenerator.nextEncryptionKey(); expect(encryptionKey.length) .toBe(policy_.symmetric.secretKey.encryption.lengthBytes); var macKey = keyGenerator.nextMacKey(); expect(macKey.length) .toBe(policy_.symmetric.secretKey.mac.lengthBytes); }); }); describe('create a symmetric cipher that should be able to ..', function() { it('encrypt and decrypt some data', function() { var encryptedData = cipher_.encrypt(data_); var decryptedData = cipher_.decrypt(encryptedData); expect(decryptedData).toEqual(data_); }); it('encrypt and decrypt some data of type string', function() { var encryptedData = cipher_.encrypt(DATA); var decryptedData = codec.utf8Decode(cipher_.decrypt(encryptedData)); expect(decryptedData).toEqual(DATA); }); it('encrypt and decrypt some data, using a specified cryptographic policy', function() { var policy = cryptoPolicy.newInstance(); policy.symmetric.secretKey.encryption.lengthBytes = cryptoPolicy.options.symmetric.secretKey.encryption.lengthBytes .KL_32; policy.symmetric.cipher.algorithm.keyLengthBytes = cryptoPolicy.options.symmetric.cipher.algorithm.AES_GCM .keyLengthBytes.KL_32; var service = symmetric.newService({policy: policy}); var key = service.newKeyGenerator().nextEncryptionKey(); var cipher = service.newCipher().init(key); var encryptedData = cipher.encrypt(data_); var decryptedData = cipher.decrypt(encryptedData); expect(decryptedData).toEqual(data_); }); it('encrypt and decrypt some data, using a specified secure random service object', function() { var secureRandomService = secureRandom.newService(); var service = symmetric.newService({secureRandomService: secureRandomService}); var keyGenerator = service.newKeyGenerator(); var key = keyGenerator.nextEncryptionKey(); var cipher = service.newCipher().init(key); var encryptedData = cipher.encrypt(data_); var decryptedData = cipher.decrypt(encryptedData); expect(decryptedData).toEqual(data_); }); it('encrypt and decrypt data that contains characters and spaces', function() { var data = codec.utf8Encode('text with some spaces'); var encryptedData = cipher_.encrypt(data); var decryptedData = cipher_.decrypt(encryptedData); expect(decryptedData).toEqual(data); }); it('encrypt and decrypt data that only contains spaces', function() { var data = codec.utf8Encode(' '); var encryptedData = cipher_.encrypt(data); var decryptedData = cipher_.decrypt(encryptedData); expect(decryptedData).toEqual(data); }); it('decrypt some data that was encrypted with the AES-GCM algorithm, using Java.', function() { var data_java = codec.utf8Encode('mUm9EQC8mrDw'); var encryptionKey_java = codec.base64Decode('v3VagUVMuZJ+b+uFSjqGTw=='); var initVector_java = codec.base64Decode('LGF26O6hR2fgUsHd'); var encryptedDataJava_java = codec.base64Decode('Cvpz8/L+mTv2eFvYpxNoxt+/KJwtAOzG7hl9Dw=='); var initVectorAndEncryptedData = bitwise.concatenate(initVector_java, encryptedDataJava_java); var cipher = service_.newCipher().init(encryptionKey_java); var decryptedData_java = cipher.decrypt(initVectorAndEncryptedData); expect(decryptedData_java).toEqual(data_java); }); it('throw an error when initializing a cipher, using a secret key with a length that differs from that of the cipher', function() { var policy = cryptoPolicy.newInstance(); policy.symmetric.secretKey.encryption.lengthBytes = cryptoPolicy.options.symmetric.secretKey.encryption.lengthBytes .KL_16; policy.symmetric.cipher.algorithm.keyLengthBytes = cryptoPolicy.options.symmetric.cipher.algorithm.AES_GCM .keyLengthBytes.KL_32; var service = symmetric.newService({policy: policy}); var key = service.newKeyGenerator().nextEncryptionKey(); var cipher = service.newCipher(); expect(function() { cipher.init(key); }).toThrow(); }); it('throw an error when encrypting some data before the cipher has been initialized with a secret key', function() { var cipher = service_.newCipher(); expect(function() { cipher.encrypt(data_); }).toThrow(); }); it('throw an error when decrypting some encrypted data before the cipher has been initialized with a secret key', function() { var cipher = service_.newCipher(); expect(function() { cipher.decrypt(encryptedData_); }).toThrow(); }); }); describe('create a MAC handler that should be able to ..', function() { it('generate and verify a MAC', function() { var mac = macHandler_.generate(data_); var verified = macHandler_.verify(mac, data_); expect(verified).toBeTruthy(); }); it('generate a MAC from multiple data parts', function() { for (var i = 0; i < dataParts_.length; i++) { macHandler_.update(dataParts_[i]); } var mac = macHandler_.generate(); var verified = macHandler_.verify( mac, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); for (var j = 0; j < (dataParts_.length - 1); j++) { macHandler_.update(dataParts_[j]); } mac = macHandler_.generate(otherData2_); verified = macHandler_.verify( mac, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); }); it('generate a MAC from multiple data parts, using method chaining', function() { var mac = macHandler_.update(data_) .update(otherData1_) .update(otherData2_) .generate(); var verified = macHandler_.verify( mac, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); mac = macHandler_.update(data_) .update(otherData1_) .generate(otherData2_); verified = macHandler_.verify( mac, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); }); it('verify a MAC generated from multiple data parts', function() { var mac = macHandler_.generate( bitwise.concatenate(data_, otherData1_, otherData2_)); for (var i = 0; i < dataParts_.length; i++) { macHandler_.update(dataParts_[i]); } var verified = macHandler_.verify(mac); expect(verified).toBeTruthy(); for (var j = 0; j < (dataParts_.length - 1); j++) { macHandler_.update(dataParts_[j]); } verified = macHandler_.verify(mac, otherData2_); expect(verified).toBeTruthy(); }); it('verify a MAC generated from multiple data parts, using method chaining', function() { var mac = macHandler_.generate( bitwise.concatenate(data_, otherData1_, otherData2_)); var verified = macHandler_.update(data_) .update(otherData1_) .update(otherData2_) .verify(mac); expect(verified).toBeTruthy(); verified = macHandler_.update(data_) .update(otherData1_) .verify(mac, otherData2_); expect(verified).toBeTruthy(); }); it('generate the expected MAC when using data of type string', function() { var mac1 = macHandler_.update(data_) .update(otherData1_) .generate(otherData2_); var mac2 = macHandler_.update(DATA) .update(OTHER_DATA_1) .generate(OTHER_DATA_2); expect(mac2).toEqual(mac1); }); it('verify a MAC when using data of type string', function() { var mac = macHandler_.update(data_).update(otherData1_).generate(otherData2_); var verified = macHandler_.update(DATA) .update(OTHER_DATA_1) .verify(mac, OTHER_DATA_2); expect(verified).toBeTruthy(); }); it('be automatically reinitialized after MAC generation', function() { var mac1 = macHandler_.generate(data_); var mac2 = macHandler_.generate(data_); expect(mac1).toEqual(mac2); }); it('generate a MAC, using a specified cryptographic policy', function() { var policy = cryptoPolicy.newInstance(); policy.symmetric.mac.hashAlgorithm = cryptoPolicy.options.symmetric.mac.hashAlgorithm.SHA512_224; var service = symmetric.newService({policy: policy}); var key = service.newKeyGenerator().nextMacKey(); var MacHandler = service.newMacHandler().init(key); var mac = MacHandler.generate(data_); var verified = MacHandler.verify(mac, data_); expect(verified).toBeTruthy(); expect(mac.length).toBe(28); }); it('generate a MAC, using a specified secure random service object', function() { var secureRandomService = secureRandom.newService(); var service = symmetric.newService({secureRandomService: secureRandomService}); var key = service.newKeyGenerator().nextMacKey(); var MacHandler = service.newMacHandler().init(key); var mac = MacHandler.generate(data_); var verified = MacHandler.verify(mac, data_); expect(verified).toBeTruthy(); }); it('throw an error when updating a MAC before the MAC handler has been initialized with a secret key', function() { var macHandler = service_.newMacHandler(); expect(function() { macHandler.update(data_); }).toThrow(); }); it('throw an error when generating a MAC before the MAC handler has been initialized with a secret key', function() { var macHandler = service_.newMacHandler(); expect(function() { macHandler.generate(data_); }).toThrow(); }); it('throw an error when verifying a MAC before the MAC handler has been initialized with a secret key', function() { var macHandler = service_.newMacHandler(); expect(function() { macHandler.verify(mac_, data_); }).toThrow(); }); it('throw an error when generating a MAC without either providing data or having previously updated the MAC handler with some data', function() { var macHandler = service_.newMacHandler().init(macKey_); expect(function() { macHandler.generate(); }).toThrow(); }); it('throw an error when verifynig a MAC without either providing data or having previously updated the MAC handler with some data', function() { var macHandler = service_.newMacHandler().init(macKey_); expect(function() { macHandler.verify(mac_); }).toThrow(); }); }); it('throw an error when initializing a cipher, using an algorithm that is not supported by the cryptographic policy', function() { var policy = cryptoPolicy.newInstance(); policy.symmetric.secretKey.encryption.lengthBytes = cryptoPolicy.options.symmetric.secretKey.encryption.lengthBytes .KL_16; policy.symmetric.cipher.algorithm.name = 'Wrong algorithm'; var service = symmetric.newService({policy: policy}); var key = service.newKeyGenerator().nextEncryptionKey(); var cipher = service.newCipher(); expect(function() { cipher.init(key); }).toThrow(); }); }); });