implement multiplayer direct connect, implement network manager, implement handshake and login packets, implement ByteBuf, implement RSA and AES encryption, bump version to 1.1.0
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import ByteBuf from "./util/ByteBuf.js";
|
||||
import PacketRegistry from "./PacketRegistry.js";
|
||||
import ProtocolState from "./ProtocolState.js";
|
||||
import {aesjs} from "../../../../../../libraries/aes.js";
|
||||
|
||||
export default class NetworkManager {
|
||||
|
||||
constructor(minecraft) {
|
||||
this.minecraft = minecraft;
|
||||
this.socket = null;
|
||||
this.connected = false;
|
||||
this.networkHandler = null;
|
||||
|
||||
this.registry = new PacketRegistry();
|
||||
this.protocolState = ProtocolState.HANDSHAKE;
|
||||
|
||||
this.readBuffer = new ByteBuf();
|
||||
this.expectedLength = -1;
|
||||
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
setNetworkHandler(networkHandler) {
|
||||
this.networkHandler = networkHandler;
|
||||
}
|
||||
|
||||
connect(address, port, proxy) {
|
||||
this.socket = new WebSocket("ws://" + proxy.address + ":" + proxy.port);
|
||||
this.socket.binaryType = "arraybuffer";
|
||||
|
||||
this.socket.onopen = e => this._onOpen(e);
|
||||
this.socket.onclose = e => this._onClose(e);
|
||||
this.socket.onmessage = e => this._onMessage(e);
|
||||
this.socket.onerror = e => this._onError(e);
|
||||
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
_onOpen() {
|
||||
this.connected = true;
|
||||
|
||||
// Send proxy handshake
|
||||
let object = {
|
||||
"address": this.address,
|
||||
"port": this.port,
|
||||
};
|
||||
this.socket.send(JSON.stringify(object));
|
||||
|
||||
// Handle connect event
|
||||
this.networkHandler.onConnect();
|
||||
|
||||
// Flush packet queue
|
||||
this.flushPacketQueue();
|
||||
}
|
||||
|
||||
sendPacket(packet) {
|
||||
if (this.connected) {
|
||||
this._sendPacketImmediately(packet);
|
||||
} else {
|
||||
this.queue.push(packet);
|
||||
}
|
||||
}
|
||||
|
||||
_sendPacketImmediately(packet) {
|
||||
// Switch packet state
|
||||
let packetState = this.registry.getPacketState(packet);
|
||||
if (packetState !== this.protocolState) {
|
||||
if (packetState === null) {
|
||||
console.error("[Network] Tried to send unknown packet: " + packet);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[Network] Switching protocol state from " + ProtocolState.getName(this.protocolState) + " to " + ProtocolState.getName(packetState));
|
||||
this.protocolState = packetState;
|
||||
}
|
||||
|
||||
// Write packet to buffer
|
||||
let buffer = new ByteBuf();
|
||||
buffer.writeByte(this.registry.getClientBoundPacketId(this.protocolState, packet));
|
||||
packet.write(buffer);
|
||||
|
||||
// Write chunk header
|
||||
let array = buffer.getArray();
|
||||
let wrapper = new ByteBuf();
|
||||
wrapper.writeVarInt(array.length);
|
||||
wrapper.write(array);
|
||||
let chunk = wrapper.getArray().buffer;
|
||||
|
||||
// Decrypt chunk
|
||||
if (this.isEncrypted) {
|
||||
chunk = this.encryption.encrypt(new Uint8Array(chunk));
|
||||
}
|
||||
|
||||
// Send chunk
|
||||
this.socket.send(chunk);
|
||||
|
||||
console.log("[Network] [OUT] " + packet.constructor.name);
|
||||
}
|
||||
|
||||
_onMessage(event) {
|
||||
let chunk = new Int8Array(event.data);
|
||||
|
||||
// Decrypt chunk
|
||||
if (this.isEncrypted) {
|
||||
chunk = this.encryption.decrypt(new Uint8Array(event.data));
|
||||
}
|
||||
|
||||
// Read packet header
|
||||
if (this.expectedLength === -1) {
|
||||
let buf = new ByteBuf(chunk);
|
||||
this.expectedLength = buf.readVarInt(); // Read packet length
|
||||
this.readBuffer.setPosition(0);
|
||||
chunk = chunk.slice(buf.getPosition());
|
||||
}
|
||||
|
||||
// Fill packet content
|
||||
this.readBuffer.write(chunk);
|
||||
|
||||
// Handle packet
|
||||
if (this.readBuffer.getPosition() >= this.expectedLength) {
|
||||
this.expectedLength = -1;
|
||||
this.readBuffer.setPosition(0);
|
||||
this.onPacketBufferReceived(this.readBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
onPacketBufferReceived(buffer) {
|
||||
let packetId = buffer.readByte(); // Read packet id
|
||||
let clazz = this.registry.getServerBoundById(this.protocolState, packetId);
|
||||
if (clazz === null) {
|
||||
console.log("[Network] Unknown packet id: " + packetId);
|
||||
return;
|
||||
}
|
||||
|
||||
let packet = new clazz;
|
||||
console.log("[Network] [IN] " + packet.constructor.name);
|
||||
|
||||
packet.read(buffer, buffer.length);
|
||||
packet.handle(this.networkHandler);
|
||||
}
|
||||
|
||||
_onError(event) {
|
||||
|
||||
}
|
||||
|
||||
_onClose(event) {
|
||||
if (this.connected) {
|
||||
this.networkHandler.onDisconnect("Disconnected from server");
|
||||
}
|
||||
|
||||
this.connected = false;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.connected = false;
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
flushPacketQueue() {
|
||||
this.queue.forEach(packet => this.sendPacket(packet));
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
enableEncryption(secretKey) {
|
||||
this.isEncrypted = true;
|
||||
this.encryption = new aesjs.ModeOfOperation.cfb(secretKey, secretKey, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export default class Packet {
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
|
||||
}
|
||||
|
||||
handle(packetHandler) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export default class PacketHandler {
|
||||
|
||||
onConnect() {
|
||||
|
||||
}
|
||||
|
||||
handleStatusResponse(packet) {
|
||||
|
||||
}
|
||||
|
||||
handleEncryptionRequest(packet) {
|
||||
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import ProtocolState from "./ProtocolState.js";
|
||||
import HandshakePacket from "./packet/handshake/client/HandshakePacket.js";
|
||||
import StatusQueryPacket from "./packet/status/client/StatusQueryPacket.js";
|
||||
import LoginStartPacket from "./packet/login/client/LoginStartPacket.js";
|
||||
import StatusResponsePacket from "./packet/status/server/StatusResponsePacket.js";
|
||||
import EncryptionRequestPacket from "./packet/login/server/EncryptionRequestPacket.js";
|
||||
import EncryptionResponsePacket from "./packet/login/client/EncryptionResponsePacket.js";
|
||||
import LoginDisconnectPacket from "./packet/login/server/LoginDisconnectPacket.js";
|
||||
|
||||
export default class PacketRegistry {
|
||||
|
||||
constructor() {
|
||||
this.packetsClient = [];
|
||||
this.packetsServer = [];
|
||||
|
||||
// Register handshake
|
||||
this.registerClient(ProtocolState.HANDSHAKE, 0x00, HandshakePacket);
|
||||
|
||||
// Register server status
|
||||
this.registerClient(ProtocolState.STATUS, 0x00, StatusQueryPacket);
|
||||
this.registerServer(ProtocolState.STATUS, 0x00, StatusResponsePacket);
|
||||
|
||||
// Register login
|
||||
this.registerServer(ProtocolState.LOGIN, 0x00, LoginDisconnectPacket);
|
||||
this.registerServer(ProtocolState.LOGIN, 0x01, EncryptionRequestPacket);
|
||||
|
||||
this.registerClient(ProtocolState.LOGIN, 0x00, LoginStartPacket);
|
||||
this.registerClient(ProtocolState.LOGIN, 0x01, EncryptionResponsePacket);
|
||||
}
|
||||
|
||||
registerClient(state, id, packet) {
|
||||
this._register(this.packetsClient, state, id, packet);
|
||||
}
|
||||
|
||||
registerServer(state, id, packet) {
|
||||
this._register(this.packetsServer, state, id, packet);
|
||||
}
|
||||
|
||||
_register(registry, state, id, packet) {
|
||||
if (typeof registry[state] === "undefined") {
|
||||
registry[state] = [];
|
||||
}
|
||||
registry[state][id] = packet;
|
||||
}
|
||||
|
||||
getServerBoundById(state, id) {
|
||||
if (typeof this.packetsServer[state][id] === "undefined") {
|
||||
return null;
|
||||
}
|
||||
return this.packetsServer[state][id];
|
||||
}
|
||||
|
||||
getClientBoundById(state, id) {
|
||||
if (typeof this.packetsClient[state][id] === "undefined") {
|
||||
return null;
|
||||
}
|
||||
return this.packetsClient[state][id];
|
||||
}
|
||||
|
||||
getClientBoundPacketId(state, packet) {
|
||||
for (let id in this.packetsClient[state]) {
|
||||
if (this.packetsClient[state][id] === packet.constructor) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getServerBoundPacketId(state, packet) {
|
||||
for (let id in this.packetsServer[state]) {
|
||||
if (this.packetsServer[state][id] === packet.constructor) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPacketState(packet) {
|
||||
for (const [state, value] of Object.entries(this.packetsClient)) {
|
||||
for (let id in value) {
|
||||
if (value[id] === packet.constructor) {
|
||||
return parseInt(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [state, value] of Object.entries(this.packetsServer)) {
|
||||
for (let id in value) {
|
||||
if (value[id] === packet.constructor) {
|
||||
return parseInt(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export default class ProtocolState {
|
||||
static HANDSHAKE = -1;
|
||||
static PLAY = 0;
|
||||
static STATUS = 1;
|
||||
static LOGIN = 2;
|
||||
|
||||
static getName(state) {
|
||||
switch (state) {
|
||||
case ProtocolState.HANDSHAKE:
|
||||
return "HANDSHAKE";
|
||||
case ProtocolState.LOGIN:
|
||||
return "LOGIN";
|
||||
case ProtocolState.PLAY:
|
||||
return "PLAY";
|
||||
case ProtocolState.STATUS:
|
||||
return "STATUS";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import PacketHandler from "../PacketHandler.js";
|
||||
import EncryptionResponsePacket from "../packet/login/client/EncryptionResponsePacket.js";
|
||||
import CryptManager from "../util/CryptManager.js";
|
||||
import GuiDisconnected from "../../gui/screens/GuiDisconnected.js";
|
||||
|
||||
export default class NetworkLoginHandler extends PacketHandler {
|
||||
|
||||
constructor(networkManager) {
|
||||
super();
|
||||
|
||||
this.networkManager = networkManager;
|
||||
}
|
||||
|
||||
handleEncryptionRequest(packet) {
|
||||
let secretKey = CryptManager.createNewSharedKey();
|
||||
this.networkManager.sendPacket(new EncryptionResponsePacket(secretKey, packet.publicKey, packet.verifyToken));
|
||||
|
||||
// Enable encryption
|
||||
this.networkManager.enableEncryption(secretKey);
|
||||
}
|
||||
|
||||
handleLoginDisconnect(packet) {
|
||||
console.log("[Network] Disconnected from server: " + packet.message);
|
||||
this.networkManager.minecraft.displayScreen(new GuiDisconnected(packet.message));
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class HandshakePacket extends Packet {
|
||||
|
||||
constructor(version, nextState) {
|
||||
super();
|
||||
|
||||
this.version = version;
|
||||
this.nextState = nextState;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.writeVarInt(this.version); // Protocol version
|
||||
buffer.writeString("localhost"); // Server address
|
||||
buffer.writeShort(25565); // Server port
|
||||
buffer.writeVarInt(this.nextState); // Next state
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
import CryptManager from "../../../util/CryptManager.js";
|
||||
|
||||
export default class EncryptionResponsePacket extends Packet {
|
||||
|
||||
constructor(secretKey, publicKey, verifyToken) {
|
||||
super();
|
||||
|
||||
this.secretKeyEncrypted = CryptManager.encryptRSA(publicKey, secretKey);
|
||||
this.verifyTokenEncrypted = CryptManager.encryptRSA(publicKey, verifyToken);
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.writeByteArray(this.secretKeyEncrypted);
|
||||
buffer.writeByteArray(this.verifyTokenEncrypted);
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class LoginStartPacket extends Packet {
|
||||
|
||||
constructor(username) {
|
||||
super();
|
||||
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.writeString(this.username);
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.username = buffer.readString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class EncryptionRequestPacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.serverId = buffer.readString();
|
||||
this.publicKey = buffer.readByteArray();
|
||||
this.verifyToken = buffer.readByteArray();
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleEncryptionRequest(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
import {format} from "../../../../../../../../../libraries/chat.js";
|
||||
|
||||
export default class LoginDisconnectPacket extends Packet {
|
||||
|
||||
constructor(message) {
|
||||
super();
|
||||
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
buffer.writeString(this.message);
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.message = format(JSON.parse(buffer.readString()));
|
||||
}
|
||||
|
||||
handle(handler) {
|
||||
handler.handleLoginDisconnect(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class StatusQueryPacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import Packet from "../../../Packet.js";
|
||||
|
||||
export default class StatusResponsePacket extends Packet {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
write(buffer) {
|
||||
|
||||
}
|
||||
|
||||
read(buffer) {
|
||||
this.object = JSON.parse(buffer.readString());
|
||||
}
|
||||
|
||||
handle(packetHandler) {
|
||||
packetHandler.handleStatusResponse(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import PacketHandler from "../PacketHandler.js";
|
||||
import GuiDisconnected from "../../gui/screens/GuiDisconnected.js";
|
||||
|
||||
export default class NetworkStatusHandler extends PacketHandler {
|
||||
|
||||
constructor(minecraft, callback) {
|
||||
super();
|
||||
|
||||
this.minecraft = minecraft;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
handleStatusResponse(packet) {
|
||||
this.callback(packet.object);
|
||||
}
|
||||
|
||||
onDisconnect() {
|
||||
this.minecraft.displayScreen(new GuiDisconnected("NetworkManager lost"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import NetworkManager from "../NetworkManager.js";
|
||||
import NetworkStatusHandler from "./NetworkStatusHandler.js";
|
||||
import Minecraft from "../../Minecraft.js";
|
||||
import HandshakePacket from "../packet/handshake/client/HandshakePacket.js";
|
||||
import ProtocolState from "../ProtocolState.js";
|
||||
import StatusQueryPacket from "../packet/status/client/StatusQueryPacket.js";
|
||||
|
||||
export default class ServerPinger {
|
||||
|
||||
constructor(minecraft) {
|
||||
this.minecraft = minecraft;
|
||||
}
|
||||
|
||||
ping(address, port, callback) {
|
||||
// Connect to server
|
||||
this.connection = new NetworkManager(this.minecraft);
|
||||
this.connection.setNetworkHandler(new NetworkStatusHandler(this.minecraft, callback));
|
||||
this.connection.connect(address, port, Minecraft.PROXY);
|
||||
|
||||
// Request status
|
||||
this.connection.sendPacket(new HandshakePacket(Minecraft.PROTOCOL_VERSION, ProtocolState.STATUS));
|
||||
this.connection.sendPacket(new StatusQueryPacket());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
export default class ByteBuf {
|
||||
|
||||
static SEGMENT_BITS = 0x7F;
|
||||
static CONTINUE_BIT = 0x80;
|
||||
|
||||
constructor(array = new Uint8Array(0)) {
|
||||
this.array = array;
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
getPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
setPosition(pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
length() {
|
||||
return this.array.length;
|
||||
}
|
||||
|
||||
getArray() {
|
||||
return this.array;
|
||||
}
|
||||
|
||||
readByte() {
|
||||
return this.array[this.pos++];
|
||||
}
|
||||
|
||||
readShort() {
|
||||
return this.array[this.pos++] << 8 | this.array[this.pos++];
|
||||
}
|
||||
|
||||
readInt() {
|
||||
return this.array[this.pos++] << 24
|
||||
| this.array[this.pos++] << 16
|
||||
| this.array[this.pos++] << 8
|
||||
| this.array[this.pos++];
|
||||
}
|
||||
|
||||
readLong() {
|
||||
return this.array[this.pos++] << 56
|
||||
| this.array[this.pos++] << 48
|
||||
| this.array[this.pos++] << 40
|
||||
| this.array[this.pos++] << 32
|
||||
| this.array[this.pos++] << 24
|
||||
| this.array[this.pos++] << 16
|
||||
| this.array[this.pos++] << 8
|
||||
| this.array[this.pos++];
|
||||
}
|
||||
|
||||
readFloat() {
|
||||
return this.readInt() / (1 << 24);
|
||||
}
|
||||
|
||||
readDouble() {
|
||||
return this.readLong() / (1 << 53);
|
||||
}
|
||||
|
||||
readString() {
|
||||
let len = this.readVarInt();
|
||||
let array = new Uint8Array(len);
|
||||
this.read(array, len);
|
||||
return new TextDecoder().decode(array);
|
||||
}
|
||||
|
||||
writeByte(value) {
|
||||
this.extendIfNeeded(1);
|
||||
this.array[this.pos++] = value;
|
||||
}
|
||||
|
||||
writeShort(value) {
|
||||
this.extendIfNeeded(2);
|
||||
this.array[this.pos++] = value >> 8;
|
||||
this.array[this.pos++] = value;
|
||||
}
|
||||
|
||||
writeInt(value) {
|
||||
this.extendIfNeeded(4);
|
||||
this.array[this.pos++] = value >> 24;
|
||||
this.array[this.pos++] = value >> 16;
|
||||
this.array[this.pos++] = value >> 8;
|
||||
this.array[this.pos++] = value;
|
||||
}
|
||||
|
||||
writeLong(value) {
|
||||
this.extendIfNeeded(8);
|
||||
this.array[this.pos++] = value >> 56;
|
||||
this.array[this.pos++] = value >> 48;
|
||||
this.array[this.pos++] = value >> 40;
|
||||
this.array[this.pos++] = value >> 32;
|
||||
this.array[this.pos++] = value >> 24;
|
||||
this.array[this.pos++] = value >> 16;
|
||||
this.array[this.pos++] = value >> 8;
|
||||
this.array[this.pos++] = value;
|
||||
}
|
||||
|
||||
writeFloat(value) {
|
||||
this.writeInt(value * (1 << 24));
|
||||
}
|
||||
|
||||
writeDouble(value) {
|
||||
this.writeLong(value * (1 << 53));
|
||||
}
|
||||
|
||||
writeString(value) {
|
||||
let array = new TextEncoder().encode(value);
|
||||
this.writeVarInt(array.length);
|
||||
this.write(array);
|
||||
}
|
||||
|
||||
writeVarInt(value) {
|
||||
while (true) {
|
||||
if ((value & ~ByteBuf.SEGMENT_BITS) === 0) {
|
||||
this.writeByte(value);
|
||||
return;
|
||||
}
|
||||
|
||||
this.writeByte((value & ByteBuf.SEGMENT_BITS) | ByteBuf.CONTINUE_BIT);
|
||||
|
||||
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||
value >>>= 7;
|
||||
}
|
||||
}
|
||||
|
||||
write(array) {
|
||||
this.extendIfNeeded(array.length);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
this.array[this.pos++] = array[i];
|
||||
}
|
||||
}
|
||||
|
||||
extendIfNeeded(bytes) {
|
||||
if (this.pos + bytes > this.array.length) {
|
||||
let newArray = new Uint8Array(this.array.length + bytes);
|
||||
newArray.set(this.array);
|
||||
this.array = newArray;
|
||||
}
|
||||
}
|
||||
|
||||
read(array, length) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = this.array[this.pos++];
|
||||
}
|
||||
}
|
||||
|
||||
readVarInt() {
|
||||
let result = 0;
|
||||
let shift = 0;
|
||||
while (true) {
|
||||
let b = this.readByte();
|
||||
if ((b & ByteBuf.CONTINUE_BIT) === 0) {
|
||||
return result | (b << shift);
|
||||
}
|
||||
result |= (b & ByteBuf.SEGMENT_BITS) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
}
|
||||
|
||||
readByteArray() {
|
||||
let length = this.readVarInt();
|
||||
let array = [];
|
||||
this.read(array, length);
|
||||
return new Int8Array(array);
|
||||
}
|
||||
|
||||
writeByteArray(array) {
|
||||
this.writeVarInt(array.length);
|
||||
this.write(array);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import Random from "../../../util/Random.js";
|
||||
import ByteBuf from "./ByteBuf.js";
|
||||
import {bigIntToBytes, bytesToBigInt, modPow} from "../../../../../../../libraries/modpow.js";
|
||||
import {parseAsn1} from "../../../../../../../libraries/asn1.js";
|
||||
|
||||
export default class CryptManager {
|
||||
|
||||
static createNewSharedKey() {
|
||||
let key = new Uint8Array(16);
|
||||
window.crypto.getRandomValues(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
static encryptRSA(publicKey, data) {
|
||||
// Parse asn1 public key
|
||||
let asn1 = parseAsn1(new Uint8Array(publicKey))
|
||||
|
||||
// Extract n an e of the public key
|
||||
let n = bytesToBigInt(asn1.children[1].children[0].children[0].value);
|
||||
let e = bytesToBigInt(asn1.children[1].children[0].children[1].value);
|
||||
|
||||
// Check length of public key
|
||||
let length = (n.toString(2).length + 7) >> 3;
|
||||
if (length < data.length + 11) {
|
||||
throw new Error("Data is too long to encrypt");
|
||||
}
|
||||
|
||||
let buffer = new ByteBuf(new Uint8Array(data).reverse());
|
||||
buffer.setPosition(buffer.length());
|
||||
|
||||
// Add padding
|
||||
buffer.writeByte(0x0);
|
||||
let random = new Random();
|
||||
let x = [];
|
||||
while (buffer.length() < length - 2) {
|
||||
x[0] = 0;
|
||||
while (x[0] === 0) random.nextBytes(x, x.length);
|
||||
buffer.writeByte(x[0]);
|
||||
}
|
||||
buffer.writeByte(0x2);
|
||||
buffer.writeByte(0x0);
|
||||
|
||||
// Reverse data
|
||||
let reversed = buffer.getArray().reverse();
|
||||
|
||||
// Convert to bigint
|
||||
let bigInt = bytesToBigInt(reversed);
|
||||
|
||||
// Encrypt
|
||||
bigInt = modPow(bigInt, e, n);
|
||||
|
||||
// Convert to bytes
|
||||
return bigIntToBytes(bigInt);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user