123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- const PluginAuth = require('./plugin-auth');
- const fs = require('fs');
- const crypto = require('crypto');
- const Errors = require('../../../misc/errors');
- const NativePasswordAuth = require('./native-password-auth');
- const Sha256PasswordAuth = require('./sha256-password-auth');
- const State = {
- INIT: 'INIT',
- FAST_AUTH_RESULT: 'FAST_AUTH_RESULT',
- REQUEST_SERVER_KEY: 'REQUEST_SERVER_KEY',
- SEND_AUTH: 'SEND_AUTH'
- };
- /**
- * Use caching Sha2 password authentication
- */
- class CachingSha2PasswordAuth extends PluginAuth {
- constructor(packSeq, compressPackSeq, pluginData, resolve, reject, multiAuthResolver) {
- super(resolve, reject, multiAuthResolver);
- this.pluginData = pluginData;
- this.sequenceNo = packSeq;
- this.counter = 0;
- this.state = State.INIT;
- }
- start(out, opts, info) {
- this.exchange(this.pluginData, out, opts, info);
- this.onPacketReceive = this.response;
- }
- exchange(buffer, out, opts, info) {
- switch (this.state) {
- case State.INIT:
- const truncatedSeed = this.pluginData.slice(0, this.pluginData.length - 1);
- const encPwd = NativePasswordAuth.encryptPassword(opts.password, truncatedSeed, 'sha256');
- out.startPacket(this);
- if (encPwd.length > 0) {
- out.writeBuffer(encPwd, 0, encPwd.length);
- out.flushBuffer(true);
- } else {
- out.writeEmptyPacket(true);
- }
- this.state = State.FAST_AUTH_RESULT;
- return;
- case State.FAST_AUTH_RESULT:
- // length encoded numeric : 0x01 0x03/0x04
- const fastAuthResult = buffer[1];
- switch (fastAuthResult) {
- case 0x03:
- // success authentication
- this.emit('send_end');
- return this.successSend(packet, out, opts, info);
- case 0x04:
- if (opts.ssl) {
- // using SSL, so sending password in clear
- out.startPacket(this);
- out.writeString(opts.password);
- out.writeInt8(0);
- out.flushBuffer(true);
- return;
- }
- // retrieve public key from configuration or from server
- if (opts.cachingRsaPublicKey) {
- try {
- let key = opts.cachingRsaPublicKey;
- if (!key.includes('-----BEGIN')) {
- // rsaPublicKey contain path
- key = fs.readFileSync(key, 'utf8');
- }
- this.publicKey = Sha256PasswordAuth.retreivePublicKey(key);
- } catch (err) {
- return this.throwError(err, info);
- }
- // send Sha256Password Packet
- Sha256PasswordAuth.sendSha256PwdPacket(
- this,
- this.pluginData,
- this.publicKey,
- opts.password,
- out
- );
- } else {
- if (!opts.allowPublicKeyRetrieval) {
- return this.throwError(
- Errors.createError(
- 'RSA public key is not available client side. Either set option `cachingRsaPublicKey` to indicate' +
- ' public key path, or allow public key retrieval with option `allowPublicKeyRetrieval`',
- true,
- info,
- '08S01',
- Errors.ER_CANNOT_RETRIEVE_RSA_KEY
- ),
- info
- );
- }
- this.state = State.REQUEST_SERVER_KEY;
- // ask caching public Key Retrieval
- out.startPacket(this);
- out.writeInt8(0x02);
- out.flushBuffer(true);
- }
- return;
- }
- case State.REQUEST_SERVER_KEY:
- this.publicKey = Sha256PasswordAuth.retreivePublicKey(buffer.toString('utf8', 1));
- this.state = State.SEND_AUTH;
- Sha256PasswordAuth.sendSha256PwdPacket(
- this,
- this.pluginData,
- this.publicKey,
- opts.password,
- out
- );
- }
- }
- static retreivePublicKey(key) {
- return key.replace('(-+BEGIN PUBLIC KEY-+\\r?\\n|\\n?-+END PUBLIC KEY-+\\r?\\n?)', '');
- }
- static sendSha256PwdPacket(cmd, pluginData, publicKey, password, out) {
- const truncatedSeed = pluginData.slice(0, pluginData.length - 1);
- out.startPacket(cmd);
- const enc = Sha256PasswordAuth.encrypt(truncatedSeed, password, publicKey);
- out.writeBuffer(enc, 0, enc.length);
- out.flushBuffer(cmd);
- }
- // encrypt password with public key
- static encrypt(seed, password, publicKey) {
- const nullFinishedPwd = Buffer.from(password + '\0');
- const xorBytes = Buffer.allocUnsafe(nullFinishedPwd.length);
- const seedLength = seed.length;
- for (let i = 0; i < xorBytes.length; i++) {
- xorBytes[i] = nullFinishedPwd[i] ^ seed[i % seedLength];
- }
- return crypto.publicEncrypt(
- { key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
- xorBytes
- );
- }
- response(packet, out, opts, info) {
- const marker = packet.peek();
- switch (marker) {
- //*********************************************************************************************************
- //* OK_Packet and Err_Packet ending packet
- //*********************************************************************************************************
- case 0x00:
- case 0xff:
- this.emit('send_end');
- return this.successSend(packet, out, opts, info);
- default:
- let promptData = packet.readBufferRemaining();
- this.exchange(promptData, out, opts, info);
- this.onPacketReceive = this.response;
- }
- }
- }
- module.exports = CachingSha2PasswordAuth;
|