auth_41.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. 'use strict';
  2. /*
  3. 4.1 authentication: (http://bazaar.launchpad.net/~mysql/mysql-server/5.5/view/head:/sql/password.c)
  4. SERVER: public_seed=create_random_string()
  5. send(public_seed)
  6. CLIENT: recv(public_seed)
  7. hash_stage1=sha1("password")
  8. hash_stage2=sha1(hash_stage1)
  9. reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
  10. // this three steps are done in scramble()
  11. send(reply)
  12. SERVER: recv(reply)
  13. hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
  14. candidate_hash2=sha1(hash_stage1)
  15. check(candidate_hash2==hash_stage2)
  16. server stores sha1(sha1(password)) ( hash_stag2)
  17. */
  18. const crypto = require('crypto');
  19. function sha1(msg, msg1, msg2) {
  20. const hash = crypto.createHash('sha1');
  21. hash.update(msg);
  22. if (msg1) {
  23. hash.update(msg1);
  24. }
  25. if (msg2) {
  26. hash.update(msg2);
  27. }
  28. return hash.digest();
  29. }
  30. function xor(a, b) {
  31. if (!Buffer.isBuffer(a)) {
  32. a = Buffer.from(a, 'binary');
  33. }
  34. if (!Buffer.isBuffer(b)) {
  35. b = Buffer.from(b, 'binary');
  36. }
  37. const result = Buffer.allocUnsafe(a.length);
  38. for (let i = 0; i < a.length; i++) {
  39. result[i] = a[i] ^ b[i];
  40. }
  41. return result;
  42. }
  43. exports.xor = xor;
  44. function token(password, scramble1, scramble2) {
  45. // TODO: use buffers (not sure why strings here)
  46. if (!password) {
  47. return Buffer.alloc(0);
  48. }
  49. const stage1 = sha1(password);
  50. return exports.calculateTokenFromPasswordSha(stage1, scramble1, scramble2);
  51. }
  52. exports.calculateTokenFromPasswordSha = function(
  53. passwordSha,
  54. scramble1,
  55. scramble2
  56. ) {
  57. // we use AUTH 41 here, and we need only the bytes we just need.
  58. const authPluginData1 = scramble1.slice(0, 8);
  59. const authPluginData2 = scramble2.slice(0, 12);
  60. const stage2 = sha1(passwordSha);
  61. const stage3 = sha1(authPluginData1, authPluginData2, stage2);
  62. return xor(stage3, passwordSha);
  63. };
  64. exports.calculateToken = token;
  65. exports.verifyToken = function(publicSeed1, publicSeed2, token, doubleSha) {
  66. const hashStage1 = xor(token, sha1(publicSeed1, publicSeed2, doubleSha));
  67. const candidateHash2 = sha1(hashStage1);
  68. return candidateHash2.compare(doubleSha) === 0;
  69. };
  70. exports.doubleSha1 = function(password) {
  71. return sha1(sha1(password));
  72. };
  73. function xorRotating(a, seed) {
  74. if (!Buffer.isBuffer(a)) {
  75. a = Buffer.from(a, 'binary');
  76. }
  77. if (!Buffer.isBuffer(seed)) {
  78. seed = Buffer.from(seed, 'binary');
  79. }
  80. const result = Buffer.allocUnsafe(a.length);
  81. const seedLen = seed.length;
  82. for (let i = 0; i < a.length; i++) {
  83. result[i] = a[i] ^ seed[i % seedLen];
  84. }
  85. return result;
  86. }
  87. exports.xorRotating = xorRotating;