Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt to Cloudflare Workers environment #2289

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 12 additions & 26 deletions lib/auth_41.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,7 @@
server stores sha1(sha1(password)) ( hash_stag2)
*/

const crypto = require('crypto');

function sha1(msg, msg1, msg2) {
const hash = crypto.createHash('sha1');
hash.update(msg);
if (msg1) {
hash.update(msg1);
}

if (msg2) {
hash.update(msg2);
}

return hash.digest();
}
const crypto = require('./utils/crypto');

function xor(a, b) {
const result = Buffer.allocUnsafe(a.length);
Expand All @@ -50,37 +36,37 @@ function xor(a, b) {

exports.xor = xor;

function token(password, scramble1, scramble2) {
async function token(password, scramble1, scramble2) {
if (!password) {
return Buffer.alloc(0);
}
const stage1 = sha1(password);
return exports.calculateTokenFromPasswordSha(stage1, scramble1, scramble2);
const stage1 = await crypto.sha1(password);
return await exports.calculateTokenFromPasswordSha(stage1, scramble1, scramble2);
}

exports.calculateTokenFromPasswordSha = function(
exports.calculateTokenFromPasswordSha = async function(
passwordSha,
scramble1,
scramble2
) {
// we use AUTH 41 here, and we need only the bytes we just need.
const authPluginData1 = scramble1.slice(0, 8);
const authPluginData2 = scramble2.slice(0, 12);
const stage2 = sha1(passwordSha);
const stage3 = sha1(authPluginData1, authPluginData2, stage2);
const stage2 = await crypto.sha1(passwordSha);
const stage3 = await crypto.sha1(authPluginData1, authPluginData2, stage2);
return xor(stage3, passwordSha);
};

exports.calculateToken = token;

exports.verifyToken = function(publicSeed1, publicSeed2, token, doubleSha) {
const hashStage1 = xor(token, sha1(publicSeed1, publicSeed2, doubleSha));
const candidateHash2 = sha1(hashStage1);
exports.verifyToken = async function(publicSeed1, publicSeed2, token, doubleSha) {
const hashStage1 = xor(token, await crypto.sha1(publicSeed1, publicSeed2, doubleSha));
const candidateHash2 = await crypto.sha1(hashStage1);
return candidateHash2.compare(doubleSha) === 0;
};

exports.doubleSha1 = function(password) {
return sha1(sha1(password));
exports.doubleSha1 = async function(password) {
return await crypto.sha1(await crypto.sha1(password));
};

function xorRotating(a, seed) {
Expand Down
32 changes: 13 additions & 19 deletions lib/auth_plugins/caching_sha2_password.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// https://mysqlserverteam.com/mysql-8-0-4-new-default-authentication-plugin-caching_sha2_password/

const PLUGIN_NAME = 'caching_sha2_password';
const crypto = require('crypto');
const crypto = require('../utils/crypto');
const { xor, xorRotating } = require('../auth_41');

const REQUEST_SERVER_KEY_PACKET = Buffer.from([2]);
Expand All @@ -15,28 +15,22 @@
const STATE_WAIT_SERVER_KEY = 2;
const STATE_FINAL = -1;

function sha256(msg) {
const hash = crypto.createHash('sha256');
hash.update(msg);
return hash.digest();
}

function calculateToken(password, scramble) {
async function calculateToken(password, scramble) {
if (!password) {
return Buffer.alloc(0);
}
const stage1 = sha256(Buffer.from(password));
const stage2 = sha256(stage1);
const stage3 = sha256(Buffer.concat([stage2, scramble]));
const stage1 = await crypto.sha256(Buffer.from(password));
const stage2 = await crypto.sha256(stage1);
const stage3 = await crypto.sha256(Buffer.concat([stage2, scramble]));
return xor(stage1, stage3);
}

function encrypt(password, scramble, key) {
async function encrypt(password, scramble, key) {
const stage1 = xorRotating(
Buffer.from(`${password}\0`, 'utf8'),
scramble
);
return crypto.publicEncrypt(key, stage1);
return await crypto.publicEncrypt(key, stage1);
}

module.exports = (pluginOptions = {}) => ({ connection }) => {
Expand All @@ -45,18 +39,18 @@

const password = connection.config.password;

const authWithKey = serverKey => {
const _password = encrypt(password, scramble, serverKey);
const authWithKey = async serverKey => {
const _password = await encrypt(password, scramble, serverKey);
state = STATE_FINAL;
return _password;
};

return data => {
return async data => {
switch (state) {
case STATE_INITIAL:
scramble = data.slice(0, 20);
state = STATE_TOKEN_SENT;
return calculateToken(password, scramble);
return await calculateToken(password, scramble);

case STATE_TOKEN_SENT:
if (FAST_AUTH_SUCCESS_PACKET.equals(data)) {
Expand All @@ -76,7 +70,7 @@

// if client provides key we can save one extra roundrip on first connection
if (pluginOptions.serverPublicKey) {
return authWithKey(pluginOptions.serverPublicKey);
return await authWithKey(pluginOptions.serverPublicKey);

Check warning on line 73 in lib/auth_plugins/caching_sha2_password.js

View check run for this annotation

Codecov / codecov/patch

lib/auth_plugins/caching_sha2_password.js#L73

Added line #L73 was not covered by tests
}

state = STATE_WAIT_SERVER_KEY;
Expand All @@ -89,7 +83,7 @@
if (pluginOptions.onServerPublicKey) {
pluginOptions.onServerPublicKey(data);
}
return authWithKey(data);
return await authWithKey(data);
case STATE_FINAL:
throw new Error(
`Unexpected data in AuthMoreData packet received by ${PLUGIN_NAME} plugin in STATE_FINAL state.`
Expand Down
6 changes: 3 additions & 3 deletions lib/auth_plugins/mysql_native_password.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ module.exports = pluginOptions => ({ connection, command }) => {
command.passwordSha1 ||
pluginOptions.passwordSha1 ||
connection.config.passwordSha1;
return data => {
return async data => {
const authPluginData1 = data.slice(0, 8);
const authPluginData2 = data.slice(8, 20);
let authToken;
if (passwordSha1) {
authToken = auth41.calculateTokenFromPasswordSha(
authToken = await auth41.calculateTokenFromPasswordSha(
passwordSha1,
authPluginData1,
authPluginData2
);
} else {
authToken = auth41.calculateToken(
authToken = await auth41.calculateToken(
password,
authPluginData1,
authPluginData2
Expand Down
16 changes: 8 additions & 8 deletions lib/auth_plugins/sha256_password.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const PLUGIN_NAME = 'sha256_password';
const crypto = require('crypto');
const crypto = require('../utils/crypto');
const { xorRotating } = require('../auth_41');

const REQUEST_SERVER_KEY_PACKET = Buffer.from([1]);
Expand All @@ -10,12 +10,12 @@
const STATE_WAIT_SERVER_KEY = 1;
const STATE_FINAL = -1;

function encrypt(password, scramble, key) {
async function encrypt(password, scramble, key) {

Check warning on line 13 in lib/auth_plugins/sha256_password.js

View check run for this annotation

Codecov / codecov/patch

lib/auth_plugins/sha256_password.js#L13

Added line #L13 was not covered by tests
const stage1 = xorRotating(
Buffer.from(`${password}\0`, 'utf8'),
scramble
);
return crypto.publicEncrypt(key, stage1);
return await crypto.publicEncrypt(key, stage1);

Check warning on line 18 in lib/auth_plugins/sha256_password.js

View check run for this annotation

Codecov / codecov/patch

lib/auth_plugins/sha256_password.js#L18

Added line #L18 was not covered by tests
}

module.exports = (pluginOptions = {}) => ({ connection }) => {
Expand All @@ -24,19 +24,19 @@

const password = connection.config.password;

const authWithKey = serverKey => {
const _password = encrypt(password, scramble, serverKey);
const authWithKey = async serverKey => {
const _password = await encrypt(password, scramble, serverKey);

Check warning on line 28 in lib/auth_plugins/sha256_password.js

View check run for this annotation

Codecov / codecov/patch

lib/auth_plugins/sha256_password.js#L27-L28

Added lines #L27 - L28 were not covered by tests
state = STATE_FINAL;
return _password;
};

return data => {
return async data => {

Check warning on line 33 in lib/auth_plugins/sha256_password.js

View check run for this annotation

Codecov / codecov/patch

lib/auth_plugins/sha256_password.js#L33

Added line #L33 was not covered by tests
switch (state) {
case STATE_INITIAL:
scramble = data.slice(0, 20);
// if client provides key we can save one extra roundrip on first connection
if (pluginOptions.serverPublicKey) {
return authWithKey(pluginOptions.serverPublicKey);
return await authWithKey(pluginOptions.serverPublicKey);

Check warning on line 39 in lib/auth_plugins/sha256_password.js

View check run for this annotation

Codecov / codecov/patch

lib/auth_plugins/sha256_password.js#L39

Added line #L39 was not covered by tests
}

state = STATE_WAIT_SERVER_KEY;
Expand All @@ -46,7 +46,7 @@
if (pluginOptions.onServerPublicKey) {
pluginOptions.onServerPublicKey(data);
}
return authWithKey(data);
return await authWithKey(data);

Check warning on line 49 in lib/auth_plugins/sha256_password.js

View check run for this annotation

Codecov / codecov/patch

lib/auth_plugins/sha256_password.js#L49

Added line #L49 was not covered by tests
case STATE_FINAL:
throw new Error(
`Unexpected data in AuthMoreData packet received by ${PLUGIN_NAME} plugin in STATE_FINAL state.`
Expand Down
6 changes: 5 additions & 1 deletion lib/commands/change_user.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@
connection.clientEncoding = CharsetToEncoding[this.charsetNumber];
// clear prepared statements cache as all statements become invalid after changeUser
connection._statements.clear();
connection.writePacket(newPacket.toPacket());
newPacket.toPacket().then(packet => {
connection.writePacket(packet);
}).catch(err => {
this.onResult(err);

Check warning on line 52 in lib/commands/change_user.js

View check run for this annotation

Codecov / codecov/patch

lib/commands/change_user.js#L52

Added line #L52 was not covered by tests
});
// check if the server supports multi-factor authentication
const multiFactorAuthentication = connection.serverCapabilityFlags & ClientConstants.MULTI_FACTOR_AUTHENTICATION;
if (multiFactorAuthentication) {
Expand Down
29 changes: 10 additions & 19 deletions lib/commands/client_handshake.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
const ClientConstants = require('../constants/client.js');
const CharsetToEncoding = require('../constants/charset_encodings.js');
const auth41 = require('../auth_41.js');
const {secureStream} = require('../stream.js');

function flagNames(flags) {
const res = [];
Expand Down Expand Up @@ -46,7 +47,7 @@
connection.writePacket(sslRequest.toPacket());
}

sendCredentials(connection) {
async sendCredentials(connection) {
if (connection.config.debug) {
// eslint-disable-next-line
console.log(
Expand Down Expand Up @@ -80,22 +81,22 @@
compress: connection.config.compress,
connectAttributes: connection.config.connectAttributes
});
connection.writePacket(handshakeResponse.toPacket());
connection.writePacket(await handshakeResponse.toPacket());
}

calculateNativePasswordAuthToken(authPluginData) {
async calculateNativePasswordAuthToken(authPluginData) {
// TODO: dont split into authPluginData1 and authPluginData2, instead join when 1 & 2 received
const authPluginData1 = authPluginData.slice(0, 8);
const authPluginData2 = authPluginData.slice(8, 20);
let authToken;
if (this.passwordSha1) {
authToken = auth41.calculateTokenFromPasswordSha(
authToken = await auth41.calculateTokenFromPasswordSha(

Check warning on line 93 in lib/commands/client_handshake.js

View check run for this annotation

Codecov / codecov/patch

lib/commands/client_handshake.js#L93

Added line #L93 was not covered by tests
this.passwordSha1,
authPluginData1,
authPluginData2
);
} else {
authToken = auth41.calculateToken(
authToken = await auth41.calculateToken(

Check warning on line 99 in lib/commands/client_handshake.js

View check run for this annotation

Codecov / codecov/patch

lib/commands/client_handshake.js#L99

Added line #L99 was not covered by tests
this.password,
authPluginData1,
authPluginData2
Expand Down Expand Up @@ -146,21 +147,11 @@
// send ssl upgrade request and immediately upgrade connection to secure
this.clientFlags |= ClientConstants.SSL;
this.sendSSLRequest(connection);
connection.startTLS(err => {
// after connection is secure
if (err) {
// SSL negotiation error are fatal
err.code = 'HANDSHAKE_SSL_ERROR';
err.fatal = true;
this.emit('error', err);
return;
}
// rest of communication is encrypted
this.sendCredentials(connection);
});
} else {
this.sendCredentials(connection);
secureStream(connection)
}
this.sendCredentials(connection).catch(err => {
this.emit('error', err);

Check warning on line 153 in lib/commands/client_handshake.js

View check run for this annotation

Codecov / codecov/patch

lib/commands/client_handshake.js#L153

Added line #L153 was not covered by tests
});
if (multiFactorAuthentication) {
// if the server supports multi-factor authentication, we enable it in
// the client
Expand Down
7 changes: 3 additions & 4 deletions lib/commands/command.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const EventEmitter = require('events').EventEmitter;
const Timers = require('timers');

class Command extends EventEmitter {
constructor() {
Expand Down Expand Up @@ -29,7 +28,7 @@ class Command extends EventEmitter {
const err = packet.asError(connection.clientEncoding);
err.sql = this.sql || this.query;
if (this.queryTimeout) {
Timers.clearTimeout(this.queryTimeout);
clearTimeout(this.queryTimeout);
this.queryTimeout = null;
}
if (this.onResult) {
Expand All @@ -45,10 +44,10 @@ class Command extends EventEmitter {
this.next = this.next(packet, connection);
if (this.next) {
return false;
}
}
this.emit('end');
return true;

}
}

Expand Down
Loading
Loading