caching-sha2-password-auth.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. const PluginAuth = require('./plugin-auth');
  2. const fs = require('fs');
  3. const crypto = require('crypto');
  4. const Errors = require('../../../misc/errors');
  5. const NativePasswordAuth = require('./native-password-auth');
  6. const Sha256PasswordAuth = require('./sha256-password-auth');
  7. const State = {
  8. INIT: 'INIT',
  9. FAST_AUTH_RESULT: 'FAST_AUTH_RESULT',
  10. REQUEST_SERVER_KEY: 'REQUEST_SERVER_KEY',
  11. SEND_AUTH: 'SEND_AUTH'
  12. };
  13. /**
  14. * Use caching Sha2 password authentication
  15. */
  16. class CachingSha2PasswordAuth extends PluginAuth {
  17. constructor(packSeq, compressPackSeq, pluginData, resolve, reject, multiAuthResolver) {
  18. super(resolve, reject, multiAuthResolver);
  19. this.pluginData = pluginData;
  20. this.sequenceNo = packSeq;
  21. this.counter = 0;
  22. this.state = State.INIT;
  23. }
  24. start(out, opts, info) {
  25. this.exchange(this.pluginData, out, opts, info);
  26. this.onPacketReceive = this.response;
  27. }
  28. exchange(buffer, out, opts, info) {
  29. switch (this.state) {
  30. case State.INIT:
  31. const truncatedSeed = this.pluginData.slice(0, this.pluginData.length - 1);
  32. const encPwd = NativePasswordAuth.encryptPassword(opts.password, truncatedSeed, 'sha256');
  33. out.startPacket(this);
  34. if (encPwd.length > 0) {
  35. out.writeBuffer(encPwd, 0, encPwd.length);
  36. out.flushBuffer(true);
  37. } else {
  38. out.writeEmptyPacket(true);
  39. }
  40. this.state = State.FAST_AUTH_RESULT;
  41. return;
  42. case State.FAST_AUTH_RESULT:
  43. // length encoded numeric : 0x01 0x03/0x04
  44. const fastAuthResult = buffer[1];
  45. switch (fastAuthResult) {
  46. case 0x03:
  47. // success authentication
  48. this.emit('send_end');
  49. return this.successSend(packet, out, opts, info);
  50. case 0x04:
  51. if (opts.ssl) {
  52. // using SSL, so sending password in clear
  53. out.startPacket(this);
  54. out.writeString(opts.password);
  55. out.writeInt8(0);
  56. out.flushBuffer(true);
  57. return;
  58. }
  59. // retrieve public key from configuration or from server
  60. if (opts.cachingRsaPublicKey) {
  61. try {
  62. let key = opts.cachingRsaPublicKey;
  63. if (!key.includes('-----BEGIN')) {
  64. // rsaPublicKey contain path
  65. key = fs.readFileSync(key, 'utf8');
  66. }
  67. this.publicKey = Sha256PasswordAuth.retreivePublicKey(key);
  68. } catch (err) {
  69. return this.throwError(err, info);
  70. }
  71. // send Sha256Password Packet
  72. Sha256PasswordAuth.sendSha256PwdPacket(
  73. this,
  74. this.pluginData,
  75. this.publicKey,
  76. opts.password,
  77. out
  78. );
  79. } else {
  80. if (!opts.allowPublicKeyRetrieval) {
  81. return this.throwError(
  82. Errors.createError(
  83. 'RSA public key is not available client side. Either set option `cachingRsaPublicKey` to indicate' +
  84. ' public key path, or allow public key retrieval with option `allowPublicKeyRetrieval`',
  85. true,
  86. info,
  87. '08S01',
  88. Errors.ER_CANNOT_RETRIEVE_RSA_KEY
  89. ),
  90. info
  91. );
  92. }
  93. this.state = State.REQUEST_SERVER_KEY;
  94. // ask caching public Key Retrieval
  95. out.startPacket(this);
  96. out.writeInt8(0x02);
  97. out.flushBuffer(true);
  98. }
  99. return;
  100. }
  101. case State.REQUEST_SERVER_KEY:
  102. this.publicKey = Sha256PasswordAuth.retreivePublicKey(buffer.toString('utf8', 1));
  103. this.state = State.SEND_AUTH;
  104. Sha256PasswordAuth.sendSha256PwdPacket(
  105. this,
  106. this.pluginData,
  107. this.publicKey,
  108. opts.password,
  109. out
  110. );
  111. }
  112. }
  113. static retreivePublicKey(key) {
  114. return key.replace('(-+BEGIN PUBLIC KEY-+\\r?\\n|\\n?-+END PUBLIC KEY-+\\r?\\n?)', '');
  115. }
  116. static sendSha256PwdPacket(cmd, pluginData, publicKey, password, out) {
  117. const truncatedSeed = pluginData.slice(0, pluginData.length - 1);
  118. out.startPacket(cmd);
  119. const enc = Sha256PasswordAuth.encrypt(truncatedSeed, password, publicKey);
  120. out.writeBuffer(enc, 0, enc.length);
  121. out.flushBuffer(cmd);
  122. }
  123. // encrypt password with public key
  124. static encrypt(seed, password, publicKey) {
  125. const nullFinishedPwd = Buffer.from(password + '\0');
  126. const xorBytes = Buffer.allocUnsafe(nullFinishedPwd.length);
  127. const seedLength = seed.length;
  128. for (let i = 0; i < xorBytes.length; i++) {
  129. xorBytes[i] = nullFinishedPwd[i] ^ seed[i % seedLength];
  130. }
  131. return crypto.publicEncrypt(
  132. { key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
  133. xorBytes
  134. );
  135. }
  136. response(packet, out, opts, info) {
  137. const marker = packet.peek();
  138. switch (marker) {
  139. //*********************************************************************************************************
  140. //* OK_Packet and Err_Packet ending packet
  141. //*********************************************************************************************************
  142. case 0x00:
  143. case 0xff:
  144. this.emit('send_end');
  145. return this.successSend(packet, out, opts, info);
  146. default:
  147. let promptData = packet.readBufferRemaining();
  148. this.exchange(promptData, out, opts, info);
  149. this.onPacketReceive = this.response;
  150. }
  151. }
  152. }
  153. module.exports = CachingSha2PasswordAuth;