deriver.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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. /* jshint node:true */
  9. 'use strict';
  10. var Policy = require('scytl-cryptopolicy');
  11. var validator = require('./input-validator');
  12. var codec = require('scytl-codec');
  13. var sjcl = require('sjcl');
  14. module.exports = PbkdfDeriver;
  15. /**
  16. * @class PbkdfDeriver
  17. * @classdesc The PBKDF deriver API. To instantiate this object, use the method
  18. * {@link PbkdfService.newDeriver}.
  19. * @hideconstructor
  20. * @param {Policy}
  21. * policy The cryptographic policy to use.
  22. */
  23. function PbkdfDeriver(policy) {
  24. var keyLength_ = policy.primitives.keyDerivation.pbkdf.keyLengthBytes;
  25. var minSaltLength_ = policy.primitives.keyDerivation.pbkdf.minSaltLengthBytes;
  26. var numIterations_ = policy.primitives.keyDerivation.pbkdf.numIterations;
  27. var hashAlgorithm_ = policy.primitives.keyDerivation.pbkdf.hashAlgorithm;
  28. /**
  29. * Derives a key, using a PBKDF.
  30. *
  31. * @function derive
  32. * @memberof PbkdfDeriver
  33. * @param {string}
  34. * password The password from which to derive the key.
  35. * @param {Uint8Array|string}
  36. * salt The salt from which to derive the key. <b>NOTE:</b> Salt
  37. * of type <code>string</code> will be UTF-8 encoded.
  38. * @returns {Uint8Array} The derived key.
  39. * @throws {Error}
  40. * If the input data validation fails.
  41. */
  42. this.derive = function(password, salt) {
  43. if (typeof salt === 'string') {
  44. salt = codec.utf8Encode(salt);
  45. }
  46. checkData(password, salt);
  47. var generateHmac = function(key) {
  48. var generator = new sjcl.misc.hmac(key, getDigester(hashAlgorithm_));
  49. this.encrypt = function() {
  50. return generator.encrypt.apply(generator, arguments);
  51. };
  52. };
  53. // NOTE: The following Base64 encoding/decoding should be replaced with the
  54. // use of 'sjcl.codec.bytes.toBits/fromBits' when/if the latter becomes part
  55. // of the default sjcl build.
  56. var derivedKey = sjcl.misc.pbkdf2(
  57. password, sjcl.codec.base64.toBits(codec.base64Encode(salt)),
  58. numIterations_, keyLength_ * 8, generateHmac);
  59. return codec.base64Decode(sjcl.codec.base64.fromBits(derivedKey));
  60. };
  61. function getDigester(algorithm) {
  62. if (algorithm ===
  63. Policy.options.primitives.keyDerivation.pbkdf.hashAlgorithm.SHA256) {
  64. return sjcl.hash.sha256;
  65. } else {
  66. throw new Error(
  67. 'Hash algorithm \'' + algorithm + '\' is not recognized.');
  68. }
  69. }
  70. function checkData(password, salt) {
  71. validator.checkIsType(
  72. password, 'string', 'Password from which to derive key');
  73. validator.checkIsInstanceOf(
  74. salt, Uint8Array, 'Uint8Array', 'Salt from which to derive key');
  75. if (salt.length < minSaltLength_) {
  76. throw new Error(
  77. 'The salt byte length ' + salt.length +
  78. ' is less than the minimum allowed salt length ' + minSaltLength_ +
  79. ' set by the cryptographic policy.');
  80. }
  81. }
  82. }