symmetric.cipher.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. * Copyright 2018 Scytl Secure Electronic Voting SA
  3. *
  4. * All rights reserved
  5. *
  6. * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
  7. */
  8. cryptolib.modules.symmetric = cryptolib.modules.symmetric || {};
  9. /**
  10. * Defines the symmetric ciphers.
  11. *
  12. * @namespace symmetric/cipher
  13. */
  14. cryptolib.modules.symmetric.cipher = function(box) {
  15. 'use strict';
  16. box.symmetric = box.symmetric || {};
  17. box.symmetric.cipher = {};
  18. /**
  19. * Allows a symmetric cipher factory to be obtained.
  20. *
  21. * @exports symmetric/cipher/factory
  22. */
  23. box.symmetric.cipher.factory = {};
  24. var policies = {
  25. cipher: {
  26. algorithm: box.policies.symmetric.cipher.algorithm,
  27. provider: box.policies.symmetric.cipher.provider,
  28. initializationVectorLengthBytes:
  29. box.policies.symmetric.cipher.initializationVectorLengthBytes
  30. }
  31. };
  32. for (var prop in policies.cipher.algorithm) {
  33. if (typeof prop !== 'undefined') {
  34. policies.cipher.algorithm = policies.cipher.algorithm[prop];
  35. }
  36. }
  37. var utils, exceptions, randomFactory;
  38. var f = function(box) {
  39. utils = box.commons.utils;
  40. exceptions = box.commons.exceptions;
  41. randomFactory =
  42. new box.primitives.securerandom.factory.SecureRandomFactory();
  43. };
  44. f.policies = {
  45. primitives: {
  46. secureRandom:
  47. {provider: box.policies.symmetric.cipher.secureRandom.provider}
  48. }
  49. };
  50. cryptolib('commons', 'primitives.securerandom', f);
  51. /**
  52. * Factory class that allows a symmetric cipher to be obtained.
  53. *
  54. * @class
  55. */
  56. box.symmetric.cipher.factory.SymmetricCipherFactory = function() {};
  57. box.symmetric.cipher.factory.SymmetricCipherFactory.prototype = {
  58. /**
  59. * Create a new symmetric cipher.
  60. * <p>
  61. * The particular cipher that is returned depends on the policies.
  62. *
  63. * @function
  64. * @returns a new symmetric cipher.
  65. */
  66. getCryptoSymmetricCipher: function() {
  67. var secureRandom;
  68. try {
  69. if (policies.cipher.provider ===
  70. Config.symmetric.cipher.provider.FORGE) {
  71. secureRandom = randomFactory.getCryptoRandomBytes();
  72. return this.getCryptoForgeSymmetricCipher(secureRandom);
  73. } else {
  74. throw new exceptions.CryptoLibException(
  75. 'No suitable provider was provided.');
  76. }
  77. } catch (error) {
  78. throw new exceptions.CryptoLibException(
  79. 'A CryptoSymmetricCipher could not be obtained.', error);
  80. }
  81. },
  82. /**
  83. * Create a new CryptoForgeSymmetricCipher.
  84. *
  85. * @function
  86. * @param secureRandom
  87. * {CryptoScytlRandomBytes} a secure source of random bytes.
  88. * @returns {symmetric/cipher.CryptoForgeSymmetricCipher} a new
  89. * CryptoForgeSymmetricCipher.
  90. */
  91. getCryptoForgeSymmetricCipher: function(secureRandom) {
  92. return new CryptoForgeSymmetricCipher(secureRandom);
  93. }
  94. };
  95. /**
  96. * @class
  97. * @param randomGenerator
  98. * {CryptoScytlRandomBytes} a secure random bytes generator.
  99. * @memberof symmetric/cipher
  100. */
  101. function CryptoForgeSymmetricCipher(randomGenerator) {
  102. this.converters = new utils.Converters();
  103. this.bitOperators = new utils.BitOperators();
  104. this.randomGenerator = randomGenerator;
  105. }
  106. CryptoForgeSymmetricCipher.prototype = {
  107. /**
  108. * Encrypts some data.
  109. *
  110. * @function
  111. * @param data
  112. * {String} Data to be encrypted, as string in Base64 encoded
  113. * format.
  114. * @param keyB64
  115. * {String} Key, as string in Base64 encoded format.
  116. * @returns Initialization vector concatenated with the encrypted data,
  117. * as string in Base64 encoded format.
  118. */
  119. encrypt: function(keyB64, dataB64) {
  120. try {
  121. var key = this.converters.base64Decode(keyB64);
  122. var data = this.converters.base64Decode(dataB64);
  123. if (key.length !== policies.cipher.algorithm.keyLengthBytes) {
  124. throw new exceptions.CryptolibException(
  125. 'Key length does not match the required by the algorithm.');
  126. }
  127. var initVec = this.randomGenerator.nextRandom(
  128. policies.cipher.initializationVectorLengthBytes);
  129. // Create a byte buffer for data.
  130. var dataBuffer = new box.forge.util.ByteBuffer(data);
  131. var cipher =
  132. box.forge.cipher.createCipher(policies.cipher.algorithm.name, key);
  133. // Only for the GCM mode
  134. var gcmAuthTagByteLength = policies.cipher.algorithm.tagLengthBytes;
  135. if (typeof gcmAuthTagBitLength !== 'undefined') {
  136. cipher.start({iv: initVec, tagLength: gcmAuthTagByteLength});
  137. } else {
  138. cipher.start({iv: initVec});
  139. }
  140. cipher.update(dataBuffer);
  141. cipher.finish();
  142. var encryptedData = cipher.output.getBytes().toString();
  143. if (typeof gcmAuthTagByteLength !== 'undefined') {
  144. var gcmAuthTag = cipher.mode.tag.getBytes();
  145. encryptedData = encryptedData + gcmAuthTag.toString();
  146. }
  147. var initVectorAndEncryptedData = initVec + encryptedData;
  148. var initVectorAndEncryptedDataBase64 =
  149. this.converters.base64Encode(initVectorAndEncryptedData);
  150. return initVectorAndEncryptedDataBase64;
  151. } catch (error) {
  152. throw new exceptions.CryptoLibException(
  153. 'Data could not be symmetrically encrypted.', error);
  154. }
  155. },
  156. /**
  157. * Decrypts some encrypted data, using the initialization vector that is
  158. * provided
  159. *
  160. * @function
  161. * @param keyB64
  162. * {String} the key as a string in Base64 encoded format.
  163. * @param initVectorAndEncryptedDataBase64
  164. * {String} Initialization vector concatenated with the
  165. * encrypted data, as string in Base64 encoded format.
  166. * @returns Decrypted data, as string in Base64 encoded format.
  167. */
  168. decrypt: function(keyB64, initVectorAndEncryptedDataBase64) {
  169. try {
  170. var key = this.converters.base64Decode(keyB64);
  171. if (key.length !== policies.cipher.algorithm.keyLengthBytes) {
  172. throw new exceptions.CryptolibException(
  173. 'Key length does not match the required by the algorithm.');
  174. }
  175. var cipher = box.forge.cipher.createDecipher(
  176. policies.cipher.algorithm.name, key);
  177. var initVectorAndEncryptedData =
  178. this.converters.base64Decode(initVectorAndEncryptedDataBase64);
  179. var initVector = this.bitOperators.extract(
  180. initVectorAndEncryptedData, 0,
  181. policies.cipher.initializationVectorLengthBytes);
  182. var encryptedData = this.bitOperators.extract(
  183. initVectorAndEncryptedData,
  184. policies.cipher.initializationVectorLengthBytes,
  185. initVectorAndEncryptedData.length);
  186. // Only for the GCM mode
  187. var gcmAuthTagByteLength = policies.cipher.algorithm.tagLengthBytes;
  188. if (typeof gcmAuthTagByteLength !== 'undefined') {
  189. var offset = encryptedData.length - gcmAuthTagByteLength;
  190. var gcmAuthTag = this.bitOperators.extract(
  191. encryptedData, offset, encryptedData.length);
  192. encryptedData = this.bitOperators.extract(encryptedData, 0, offset);
  193. var gcmAuthTagBitLength =
  194. gcmAuthTagByteLength * box.NUMBER_OF_BITS_PER_BYTE;
  195. cipher.start({
  196. iv: initVector,
  197. tagLength: gcmAuthTagBitLength,
  198. tag: gcmAuthTag
  199. });
  200. } else {
  201. cipher.start({iv: initVector});
  202. }
  203. var encryptedDataBuffer = new box.forge.util.ByteBuffer(encryptedData);
  204. cipher.update(encryptedDataBuffer);
  205. cipher.finish();
  206. var outputBase64 =
  207. this.converters.base64Encode(cipher.output.getBytes().toString());
  208. return outputBase64;
  209. } catch (error) {
  210. throw new exceptions.CryptoLibException(
  211. 'Data could not be symmetrically decrypted.', error);
  212. }
  213. }
  214. };
  215. };