You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
5.7 KiB

4 years ago
  1. /**
  2. * Password-Based Key-Derivation Function #2 implementation.
  3. *
  4. * See RFC 2898 for details.
  5. *
  6. * @author Dave Longley
  7. *
  8. * Copyright (c) 2010-2013 Digital Bazaar, Inc.
  9. */
  10. var forge = require('./forge');
  11. require('./hmac');
  12. require('./md');
  13. require('./util');
  14. var pkcs5 = forge.pkcs5 = forge.pkcs5 || {};
  15. var crypto;
  16. if(forge.util.isNodejs && !forge.options.usePureJavaScript) {
  17. crypto = require('crypto');
  18. }
  19. /**
  20. * Derives a key from a password.
  21. *
  22. * @param p the password as a binary-encoded string of bytes.
  23. * @param s the salt as a binary-encoded string of bytes.
  24. * @param c the iteration count, a positive integer.
  25. * @param dkLen the intended length, in bytes, of the derived key,
  26. * (max: 2^32 - 1) * hash length of the PRF.
  27. * @param [md] the message digest (or algorithm identifier as a string) to use
  28. * in the PRF, defaults to SHA-1.
  29. * @param [callback(err, key)] presence triggers asynchronous version, called
  30. * once the operation completes.
  31. *
  32. * @return the derived key, as a binary-encoded string of bytes, for the
  33. * synchronous version (if no callback is specified).
  34. */
  35. module.exports = forge.pbkdf2 = pkcs5.pbkdf2 = function(
  36. p, s, c, dkLen, md, callback) {
  37. if(typeof md === 'function') {
  38. callback = md;
  39. md = null;
  40. }
  41. // use native implementation if possible and not disabled, note that
  42. // some node versions only support SHA-1, others allow digest to be changed
  43. if(forge.util.isNodejs && !forge.options.usePureJavaScript &&
  44. crypto.pbkdf2 && (md === null || typeof md !== 'object') &&
  45. (crypto.pbkdf2Sync.length > 4 || (!md || md === 'sha1'))) {
  46. if(typeof md !== 'string') {
  47. // default prf to SHA-1
  48. md = 'sha1';
  49. }
  50. p = Buffer.from(p, 'binary');
  51. s = Buffer.from(s, 'binary');
  52. if(!callback) {
  53. if(crypto.pbkdf2Sync.length === 4) {
  54. return crypto.pbkdf2Sync(p, s, c, dkLen).toString('binary');
  55. }
  56. return crypto.pbkdf2Sync(p, s, c, dkLen, md).toString('binary');
  57. }
  58. if(crypto.pbkdf2Sync.length === 4) {
  59. return crypto.pbkdf2(p, s, c, dkLen, function(err, key) {
  60. if(err) {
  61. return callback(err);
  62. }
  63. callback(null, key.toString('binary'));
  64. });
  65. }
  66. return crypto.pbkdf2(p, s, c, dkLen, md, function(err, key) {
  67. if(err) {
  68. return callback(err);
  69. }
  70. callback(null, key.toString('binary'));
  71. });
  72. }
  73. if(typeof md === 'undefined' || md === null) {
  74. // default prf to SHA-1
  75. md = 'sha1';
  76. }
  77. if(typeof md === 'string') {
  78. if(!(md in forge.md.algorithms)) {
  79. throw new Error('Unknown hash algorithm: ' + md);
  80. }
  81. md = forge.md[md].create();
  82. }
  83. var hLen = md.digestLength;
  84. /* 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and
  85. stop. */
  86. if(dkLen > (0xFFFFFFFF * hLen)) {
  87. var err = new Error('Derived key is too long.');
  88. if(callback) {
  89. return callback(err);
  90. }
  91. throw err;
  92. }
  93. /* 2. Let len be the number of hLen-octet blocks in the derived key,
  94. rounding up, and let r be the number of octets in the last
  95. block:
  96. len = CEIL(dkLen / hLen),
  97. r = dkLen - (len - 1) * hLen. */
  98. var len = Math.ceil(dkLen / hLen);
  99. var r = dkLen - (len - 1) * hLen;
  100. /* 3. For each block of the derived key apply the function F defined
  101. below to the password P, the salt S, the iteration count c, and
  102. the block index to compute the block:
  103. T_1 = F(P, S, c, 1),
  104. T_2 = F(P, S, c, 2),
  105. ...
  106. T_len = F(P, S, c, len),
  107. where the function F is defined as the exclusive-or sum of the
  108. first c iterates of the underlying pseudorandom function PRF
  109. applied to the password P and the concatenation of the salt S
  110. and the block index i:
  111. F(P, S, c, i) = u_1 XOR u_2 XOR ... XOR u_c
  112. where
  113. u_1 = PRF(P, S || INT(i)),
  114. u_2 = PRF(P, u_1),
  115. ...
  116. u_c = PRF(P, u_{c-1}).
  117. Here, INT(i) is a four-octet encoding of the integer i, most
  118. significant octet first. */
  119. var prf = forge.hmac.create();
  120. prf.start(md, p);
  121. var dk = '';
  122. var xor, u_c, u_c1;
  123. // sync version
  124. if(!callback) {
  125. for(var i = 1; i <= len; ++i) {
  126. // PRF(P, S || INT(i)) (first iteration)
  127. prf.start(null, null);
  128. prf.update(s);
  129. prf.update(forge.util.int32ToBytes(i));
  130. xor = u_c1 = prf.digest().getBytes();
  131. // PRF(P, u_{c-1}) (other iterations)
  132. for(var j = 2; j <= c; ++j) {
  133. prf.start(null, null);
  134. prf.update(u_c1);
  135. u_c = prf.digest().getBytes();
  136. // F(p, s, c, i)
  137. xor = forge.util.xorBytes(xor, u_c, hLen);
  138. u_c1 = u_c;
  139. }
  140. /* 4. Concatenate the blocks and extract the first dkLen octets to
  141. produce a derived key DK:
  142. DK = T_1 || T_2 || ... || T_len<0..r-1> */
  143. dk += (i < len) ? xor : xor.substr(0, r);
  144. }
  145. /* 5. Output the derived key DK. */
  146. return dk;
  147. }
  148. // async version
  149. var i = 1, j;
  150. function outer() {
  151. if(i > len) {
  152. // done
  153. return callback(null, dk);
  154. }
  155. // PRF(P, S || INT(i)) (first iteration)
  156. prf.start(null, null);
  157. prf.update(s);
  158. prf.update(forge.util.int32ToBytes(i));
  159. xor = u_c1 = prf.digest().getBytes();
  160. // PRF(P, u_{c-1}) (other iterations)
  161. j = 2;
  162. inner();
  163. }
  164. function inner() {
  165. if(j <= c) {
  166. prf.start(null, null);
  167. prf.update(u_c1);
  168. u_c = prf.digest().getBytes();
  169. // F(p, s, c, i)
  170. xor = forge.util.xorBytes(xor, u_c, hLen);
  171. u_c1 = u_c;
  172. ++j;
  173. return forge.util.setImmediate(inner);
  174. }
  175. /* 4. Concatenate the blocks and extract the first dkLen octets to
  176. produce a derived key DK:
  177. DK = T_1 || T_2 || ... || T_len<0..r-1> */
  178. dk += (i < len) ? xor : xor.substr(0, r);
  179. ++i;
  180. outer();
  181. }
  182. outer();
  183. };