primitives.derivation.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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.primitives = cryptolib.modules.primitives || {};
  9. /** @namespace primitives/derivation */
  10. cryptolib.modules.primitives.derivation = function(box) {
  11. 'use strict';
  12. box.primitives = box.primitives || {};
  13. box.primitives.derivation = {};
  14. /**
  15. * A module that holds derivation functionalities.
  16. *
  17. * @exports primitives/derivation
  18. */
  19. box.primitives.derivation.factory = {};
  20. var policies = {
  21. pbkdf: {
  22. provider: box.policies.primitives.derivation.pbkdf.provider,
  23. hash: box.policies.primitives.derivation.pbkdf.hash,
  24. saltLengthBytes: box.policies.primitives.derivation.pbkdf.saltLengthBytes,
  25. keyLengthBytes: box.policies.primitives.derivation.pbkdf.keyLengthBytes,
  26. iterations: box.policies.primitives.derivation.pbkdf.iterations
  27. }
  28. };
  29. var exceptions, converters;
  30. cryptolib('rnd', 'commons', function(box) {
  31. exceptions = box.commons.exceptions;
  32. converters = new box.commons.utils.Converters();
  33. });
  34. /**
  35. * A factory class for generating {@link CryptoForgePBKDFSecretKeyGenerator}
  36. * objects.
  37. *
  38. * @class
  39. */
  40. box.primitives.derivation.factory.SecretKeyGeneratorFactory = function() {
  41. };
  42. box.primitives.derivation.factory.SecretKeyGeneratorFactory.prototype = {
  43. /**
  44. * Creates a generator for deriving passwords.
  45. *
  46. * @function
  47. * @returns {primitives/derivation.CryptoForgePBKDFSecretKeyGenerator} a
  48. * {@link primitives/derivation.CryptoForgePBKDFSecretKeyGenerator}
  49. * object.
  50. */
  51. createPBKDF: function() {
  52. var provider = policies.pbkdf.provider;
  53. try {
  54. if (provider === Config.primitives.derivation.pbkdf.provider.FORGE) {
  55. return new CryptoForgePBKDFSecretKeyGenerator();
  56. } else {
  57. throw new exceptions.CryptoLibException(
  58. 'The specified provider ' + provider + ' is not supported.');
  59. }
  60. } catch (error) {
  61. throw new exceptions.CryptoLibException(
  62. 'A CryptoPBKDFSecretKeyGenerator object could not be created.',
  63. error);
  64. }
  65. }
  66. };
  67. /**
  68. *
  69. * @class
  70. * @memberof primitives/derivation
  71. */
  72. function CryptoForgePBKDFSecretKeyGenerator() {
  73. validateHash(policies.pbkdf.hash);
  74. validateKeyLengthBytes(policies.pbkdf.keyLengthBytes);
  75. validateSaltLengthBytes(policies.pbkdf.saltLengthBytes);
  76. validateIterations(policies.pbkdf.iterations);
  77. this.keyLengthBytes = policies.pbkdf.keyLengthBytes;
  78. }
  79. CryptoForgePBKDFSecretKeyGenerator.prototype = {
  80. /**
  81. * @param {string}
  82. * password the password, as a string of bytes in Base64
  83. * encoded format.
  84. * @param {string}
  85. * salt the salt, as a string of bytes in Base64 encoded
  86. * format. Note that the salt length should coincide with the
  87. * one required by the hash algorithm.
  88. *
  89. * @return {string} the derived key, as a string of bytes in Base64
  90. * encoded format.
  91. */
  92. generateSecret: function(passwordB64, saltB64) {
  93. var password = converters.base64Decode(passwordB64);
  94. var salt = converters.base64Decode(saltB64);
  95. if (salt.length < policies.pbkdf.saltLengthBytes) {
  96. throw new exceptions.CryptoLibException(
  97. 'The salt byte length ' + salt.length +
  98. ' is less than the minimum allowed salt length ' +
  99. policies.pbkdf.saltLengthBytes +
  100. ' set by the cryptographic policy.');
  101. }
  102. var derivedKey = sjcl.misc.pbkdf2(
  103. password, sjcl.codec.base64.toBits(saltB64),
  104. policies.pbkdf.iterations, this.keyLengthBytes * 8,
  105. hashCallback(policies.pbkdf.hash));
  106. return sjcl.codec.base64.fromBits(derivedKey);
  107. }
  108. };
  109. function hashCallback(algorithm) {
  110. if (algorithm === Config.primitives.derivation.pbkdf.hash.SHA256) {
  111. return null;
  112. } else {
  113. throw new exceptions.CryptoLibException(
  114. 'Hash algorithm \'' + algorithm + '\' is unsupported');
  115. }
  116. }
  117. function validateHash(hash) {
  118. var supportedHashAlgorithms =
  119. [Config.primitives.derivation.pbkdf.hash.SHA256];
  120. if (supportedHashAlgorithms.indexOf(hash) < 0) {
  121. throw new exceptions.CryptoLibException(
  122. 'The specified hash algorithm ' + hash +
  123. ' is not one of the supported options: ' + supportedHashAlgorithms);
  124. }
  125. }
  126. function validateSaltLengthBytes(saltLengthBytes) {
  127. var supportedSaltLengthBytes = [
  128. Config.primitives.derivation.pbkdf.saltLengthBytes.SL_20,
  129. Config.primitives.derivation.pbkdf.saltLengthBytes.SL_32
  130. ];
  131. if (typeof saltLengthBytes !== 'number' || saltLengthBytes % 1 !== 0 ||
  132. saltLengthBytes < 0) {
  133. throw new exceptions.CryptoLibException(
  134. 'The specified salt byte length ' + saltLengthBytes +
  135. ' is not a positive integer.');
  136. }
  137. if (supportedSaltLengthBytes.indexOf(saltLengthBytes) < 0) {
  138. throw new exceptions.CryptoLibException(
  139. 'The specified salt byte length ' + saltLengthBytes +
  140. ' is not one of the supported options: ' + supportedSaltLengthBytes);
  141. }
  142. }
  143. function validateKeyLengthBytes(keyLengthBytes) {
  144. var supportedKeyLengthBytes = [
  145. Config.primitives.derivation.pbkdf.keyLengthBytes.KL_16,
  146. Config.primitives.derivation.pbkdf.keyLengthBytes.KL_32
  147. ];
  148. if (typeof keyLengthBytes !== 'number' || keyLengthBytes % 1 !== 0 ||
  149. keyLengthBytes < 0) {
  150. throw new exceptions.CryptoLibException(
  151. 'The specified key byte length ' + keyLengthBytes +
  152. ' is not a positive number.');
  153. }
  154. if (supportedKeyLengthBytes.indexOf(keyLengthBytes) < 0) {
  155. throw new exceptions.CryptoLibException(
  156. 'The specified key byte length ' + keyLengthBytes +
  157. ' is not one of the supported options: ' + supportedKeyLengthBytes);
  158. }
  159. }
  160. function validateIterations(iterations) {
  161. var supportedIterations = [
  162. Config.primitives.derivation.pbkdf.iterations.I_1,
  163. Config.primitives.derivation.pbkdf.iterations.I_8000,
  164. Config.primitives.derivation.pbkdf.iterations.I_16000,
  165. Config.primitives.derivation.pbkdf.iterations.I_32000,
  166. Config.primitives.derivation.pbkdf.iterations.I_64000
  167. ];
  168. if (typeof iterations !== 'number' || iterations % 1 !== 0 ||
  169. iterations < 0) {
  170. throw new exceptions.CryptoLibException(
  171. 'The specified number of iterations ' + iterations +
  172. ' is not a positive integer.');
  173. }
  174. if (supportedIterations.indexOf(iterations) < 0) {
  175. throw new exceptions.CryptoLibException(
  176. 'The specified number of iterations ' + iterations +
  177. ' is not one of the supported options: ' + supportedIterations);
  178. }
  179. }
  180. };