143 lines
4.3 KiB
JavaScript
143 lines
4.3 KiB
JavaScript
|
const PluginAuth = require('./plugin-auth');
|
||
|
const fs = require('fs');
|
||
|
const crypto = require('crypto');
|
||
|
const Errors = require('../../../misc/errors');
|
||
|
|
||
|
/**
|
||
|
* Use Sha256 authentication
|
||
|
*/
|
||
|
class Sha256PasswordAuth extends PluginAuth {
|
||
|
constructor(packSeq, compressPackSeq, pluginData, resolve, reject, multiAuthResolver) {
|
||
|
super(resolve, reject, multiAuthResolver);
|
||
|
this.pluginData = pluginData;
|
||
|
this.sequenceNo = packSeq;
|
||
|
this.counter = 0;
|
||
|
this.initialState = true;
|
||
|
}
|
||
|
|
||
|
start(out, opts, info) {
|
||
|
this.exchange(this.pluginData, out, opts, info);
|
||
|
this.onPacketReceive = this.response;
|
||
|
}
|
||
|
|
||
|
exchange(buffer, out, opts, info) {
|
||
|
if (this.initialState) {
|
||
|
if (!opts.password) {
|
||
|
out.startPacket(this);
|
||
|
out.writeEmptyPacket(true);
|
||
|
return;
|
||
|
} else if (opts.ssl) {
|
||
|
// using SSL, so sending password in clear
|
||
|
out.startPacket(this);
|
||
|
if (opts.password) {
|
||
|
out.writeString(opts.password);
|
||
|
}
|
||
|
out.writeInt8(0);
|
||
|
out.flushBuffer(true);
|
||
|
return;
|
||
|
} else {
|
||
|
// retrieve public key from configuration or from server
|
||
|
if (opts.rsaPublicKey) {
|
||
|
try {
|
||
|
let key = opts.rsaPublicKey;
|
||
|
if (!key.includes('-----BEGIN')) {
|
||
|
// rsaPublicKey contain path
|
||
|
key = fs.readFileSync(key, 'utf8');
|
||
|
}
|
||
|
this.publicKey = Sha256PasswordAuth.retreivePublicKey(key);
|
||
|
} catch (err) {
|
||
|
return this.throwError(err, info);
|
||
|
}
|
||
|
} else {
|
||
|
if (!opts.allowPublicKeyRetrieval) {
|
||
|
return this.throwError(
|
||
|
Errors.createError(
|
||
|
'RSA public key is not available client side. Either set option `rsaPublicKey` to indicate' +
|
||
|
' public key path, or allow public key retrieval with option `allowPublicKeyRetrieval`',
|
||
|
|
||
|
true,
|
||
|
info,
|
||
|
'08S01',
|
||
|
Errors.ER_CANNOT_RETRIEVE_RSA_KEY
|
||
|
),
|
||
|
info
|
||
|
);
|
||
|
}
|
||
|
this.initialState = false;
|
||
|
|
||
|
// ask public Key Retrieval
|
||
|
out.startPacket(this);
|
||
|
out.writeInt8(0x01);
|
||
|
out.flushBuffer(true);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// send Sha256Password Packet
|
||
|
Sha256PasswordAuth.sendSha256PwdPacket(
|
||
|
this,
|
||
|
this.pluginData,
|
||
|
this.publicKey,
|
||
|
opts.password,
|
||
|
out
|
||
|
);
|
||
|
} else {
|
||
|
// has request public key
|
||
|
this.publicKey = Sha256PasswordAuth.retreivePublicKey(buffer.toString('utf8', 1));
|
||
|
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 = Sha256PasswordAuth;
|