Comparing version 1.2.1 to 1.2.2
12
index.js
@@ -21,3 +21,3 @@ | ||
if (!data.outbound) | ||
throw ("outbound is not defined") | ||
throw ("outbound is not defined (in bridge mode outbound must be defined)") | ||
if (data.outbound.protocol) | ||
@@ -33,10 +33,12 @@ throw ("do not use outbound protocol in bridge mode") | ||
for (const i of data.inbounds) { | ||
localNetwork(i.networks, protocols[i.protocol].server(i.users, freedom)) | ||
if (!i.protocol || !(i.protocol in protocols)) | ||
throw ("inbound protocol is not defined") | ||
localNetwork(i.networks, protocols[i.protocol].server(i, freedom)) | ||
} | ||
} else { | ||
var remoteProtocol = protocols[data.outbound.protocol].client(data.outbound.user, remoteNetwork(data.outbound.network.type)) | ||
var remoteProtocol = protocols[data.outbound.protocol].client(data.outbound, remoteNetwork(data.outbound.network.type)) | ||
for (const i of data.inbounds) { | ||
if (!i.protocol) | ||
if (!i.protocol || !(i.protocol in protocols)) | ||
throw ("inbound protocol is not defined") | ||
localNetwork(i.networks, protocols[i.protocol].server(i.users, remoteProtocol)) | ||
localNetwork(i.networks, protocols[i.protocol].server(i, remoteProtocol)) | ||
} | ||
@@ -43,0 +45,0 @@ } |
{ | ||
"name": "js2ray", | ||
"version": "1.2.1", | ||
"version": "1.2.2", | ||
"description": "The v2ray vmess protocol, based on nodejs javascript which you can use on hosts and servers", | ||
@@ -23,12 +23,13 @@ "main": "index.js", | ||
"license": "MIT", | ||
"homepage": "https://github.com/seezaara/js2ray", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/seezaara/js2ray.git" | ||
}, | ||
"dependencies": { | ||
"cbor-js": "^0.1.0", | ||
"cuckoo-filter": "^1.1.4", | ||
"libsodium-wrappers": "^0.7.11", | ||
"ws": "^8.12.0" | ||
}, | ||
"homepage": "https://github.com/seezaara/js2ray", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/seezaara/js2ray.git" | ||
} | ||
} | ||
} |
@@ -85,13 +85,13 @@ | ||
function user(inp, del) { | ||
function user(tag, inp, del) { | ||
if (typeof inp == 'object') { | ||
addUser(a) | ||
addUser(tag, a) | ||
save() | ||
} else if (typeof inp == "string") { | ||
var sub_user = [] | ||
for (var i in users) { | ||
if (users[i].email == inp) { | ||
sub_user.push(users[i]) | ||
for (var i in usersp[tag]) { | ||
if (usersp[tag][i].email == inp) { | ||
sub_user.push(usersp[tag][i]) | ||
if (del) | ||
users.splice(i, 1) | ||
usersp[tag].splice(i, 1) | ||
} | ||
@@ -101,24 +101,30 @@ } | ||
} else if (inp == undefined) { | ||
return users; | ||
return usersp[tag]; | ||
} | ||
} | ||
function AsAccount(u) { | ||
function AsAccount(tag, data_user) { | ||
if (tag == undefined) | ||
tag = (Math.random() + 1).toString(36).substring(3) | ||
if (users[tag] == undefined) | ||
users[tag] = [] | ||
var users_ids = [] | ||
for (var i in users) { | ||
users_ids.push(users[i].id.UUID.toString()) | ||
for (var i in users[tag]) { | ||
users_ids.push(users[tag][i].id.UUID.toString()) | ||
} | ||
for (var i of u) { | ||
for (var i of data_user) { | ||
if (!users_ids.includes(ParseString(i.id).toString())) { | ||
addUser(i, true) | ||
addUser(tag, i) | ||
} | ||
} | ||
save() | ||
return users; | ||
return users[tag]; | ||
} | ||
function addUser(a) { | ||
function addUser(tag, a) { | ||
var protoID = NewID(ParseString(a.id)) | ||
if (!a.alterId) | ||
a.alterId = 0 | ||
var user = { | ||
@@ -136,3 +142,3 @@ id: protoID, | ||
} | ||
users.push(user) | ||
users[tag].push(user) | ||
return user | ||
@@ -144,6 +150,7 @@ } | ||
function usesave() { | ||
return {} | ||
try { | ||
var usage = fs.readFileSync(__dirname + "/data.json"); | ||
if (usage == "") { | ||
usage = [] | ||
usage = {} | ||
} else { | ||
@@ -164,3 +171,3 @@ usage = JSON.parse(usage) | ||
throw Error("read file error: ") | ||
return [] | ||
return {} | ||
} | ||
@@ -167,0 +174,0 @@ } |
@@ -21,2 +21,11 @@ | ||
function CreateAuthID(cmdKey, time) { | ||
var buf = Buffer.alloc(16) | ||
buf.writeBigUInt64BE(BigInt(time), 0) | ||
buf.writeInt32BE(Math.floor(Math.random() * 147483647), 8) | ||
buf.writeUInt32BE(CRC32(buf.subarray(0, 12)), 12) | ||
var aesBlock = kdf.KDF16(cmdKey, consts.KDFSaltConstAuthIDEncryptionKey) | ||
return kdf.encrypt(buf, aesBlock) | ||
} | ||
function Decode(aidd, data) { | ||
@@ -32,2 +41,5 @@ data = kdf.decrypt(data, aidd) | ||
function unix() { | ||
return Math.round(new Date() / 1000) | ||
} | ||
function Match(AuthIDDecoderHolder, authID) { | ||
@@ -43,3 +55,3 @@ for (var v in AuthIDDecoderHolder.decoders) { | ||
if (Math.abs(Number(decode.t) - (Math.round(new Date() / 1000))) > 120) { | ||
if (Math.abs(Number(decode.t) - unix()) > 120) { | ||
continue | ||
@@ -80,3 +92,2 @@ } | ||
// ============================= | ||
@@ -106,2 +117,49 @@ var payloadHeaderAEADKey = kdf.KDF16(key, consts.KDFSaltConstVMessHeaderPayloadAEADKey, authid, nonce) | ||
function SealVMessAEADHeader(key, data) { | ||
const generatedAuthID = CreateAuthID(key, unix()) | ||
const connectionNonce = crypto.randomBytes(8) | ||
const aeadPayloadLengthSerializeBuffer = new Buffer.alloc(2); | ||
aeadPayloadLengthSerializeBuffer.writeUInt16BE(data.length, 0); | ||
// ================================== | ||
var payloadHeaderLengthAEADKey = kdf.KDF16(key, consts.KDFSaltConstVMessHeaderPayloadLengthAEADKey, generatedAuthID, connectionNonce) | ||
var payloadHeaderLengthAEADNonce = kdf.KDF(key, consts.KDFSaltConstVMessHeaderPayloadLengthAEADIV, generatedAuthID, connectionNonce).subarray(0, 12) | ||
try { | ||
const cipher = crypto.createCipheriv('aes-128-gcm', payloadHeaderLengthAEADKey, payloadHeaderLengthAEADNonce); | ||
cipher.setAAD(generatedAuthID); | ||
var payloadHeaderLengthAEADEncrypted = Buffer.concat([cipher.update(aeadPayloadLengthSerializeBuffer), cipher.final(), cipher.getAuthTag()]) | ||
} catch (error) { | ||
log(error) | ||
return | ||
} | ||
// ================================== | ||
var payloadHeaderAEADKey = kdf.KDF16(key, consts.KDFSaltConstVMessHeaderPayloadAEADKey, generatedAuthID, connectionNonce) | ||
var payloadHeaderAEADNonce = kdf.KDF(key, consts.KDFSaltConstVMessHeaderPayloadAEADIV, generatedAuthID, connectionNonce).subarray(0, 12) | ||
try { | ||
const cipher = crypto.createCipheriv('aes-128-gcm', payloadHeaderAEADKey, payloadHeaderAEADNonce); | ||
cipher.setAAD(generatedAuthID); | ||
var payloadHeaderAEADEncrypted = Buffer.concat([cipher.update(data), cipher.final(), cipher.getAuthTag()]) | ||
} catch (error) { | ||
log(error) | ||
return | ||
} | ||
// =========================== | ||
return Buffer.concat([ | ||
generatedAuthID, | ||
payloadHeaderLengthAEADEncrypted, | ||
connectionNonce, | ||
payloadHeaderAEADEncrypted | ||
]) | ||
} | ||
// ====================================================== FUNCTIONS | ||
@@ -113,3 +171,4 @@ | ||
Match, | ||
OpenVMessAEADHeader | ||
OpenVMessAEADHeader, | ||
SealVMessAEADHeader | ||
} |
const common = require("./common") | ||
const validator = require("./validator") | ||
const consts = require("./consts") | ||
const AdvancedBuffer = require("./advanced-buffer") | ||
const kdf = require("./kdf") | ||
const net = require("net") | ||
const ipparser = require("ip") | ||
const crypto = require("crypto"); | ||
@@ -12,16 +16,37 @@ const ATYP_V4 = 0x01; | ||
function init(users, remoteProtocol) { | ||
var checkuser = validator.init([users]) | ||
return function (address, port, cmd, remoteConnect, remoteMessage, remoteClose) { | ||
var app = {} | ||
function init(data, remoteNetwork) { | ||
var randomuser = validator.init(data).get | ||
return function (address, port, cmd, localConnect, localMessage, localClose) { | ||
const app = {} | ||
app.localMessage = localMessage | ||
app.user = {} | ||
app._cmd = cmd; | ||
app._port = common.ntb(port); | ||
const type = getAddrType(address); | ||
app._atyp = type; | ||
app._port = ntb(port); | ||
app._host = (type === ATYP_DOMAIN) ? Buffer.from(address) : ipparser.toBuffer(address); | ||
EncodeRequestHeader(cmd) | ||
app._host = (type === ATYP_DOMAIN) ? Buffer.from(address) : toBuffer(address); | ||
app._adBuf = new AdvancedBuffer({ getPacketLength: onReceivingLength.bind(app) }); | ||
app._adBuf.on('data', EncodeRequestBody.bind(app)); | ||
app._staging = Buffer.alloc(0) | ||
app._option = 0x05 | ||
app._cipherNonce = 0; | ||
app._decipherNonce = 0; | ||
app._adBuf = new AdvancedBuffer({ getPacketLength: onReceivingLength }); | ||
app._adBuf.on('data', DecodeResponseBody); | ||
var socket = remoteNetwork( | ||
data.network.address, | ||
data.network.port, | ||
data.network.option, | ||
localConnect, | ||
DecodeResponseHeader.bind(app), | ||
localClose, | ||
) | ||
socket.app = app | ||
return { | ||
message: EncodeRequestHeader, | ||
message: EncodeRequestBody.bind(socket, randomuser), | ||
close: onclose.bind(socket), | ||
@@ -32,52 +57,85 @@ } | ||
function onclose() { | ||
this.close() | ||
const app = this.app | ||
app._isConnecting = false; | ||
app._isHeaderSent = false; | ||
app._isHeaderRecv = false; | ||
function getAddrType(host) { | ||
if (net.isIPv4(host)) { | ||
return ATYP_V4; | ||
app._adBuf.clear(); | ||
app._adBuf = null; | ||
app._host = null; | ||
app._port = null; | ||
app._staging = null; | ||
app._dataEncKey = null; | ||
app._dataEncKeyForChaCha20 = null; | ||
app._dataEncIV = null; | ||
app._dataDecKey = null; | ||
app._dataDecKeyForChaCha20 = null; | ||
app._dataDecIV = null; | ||
app._chunkLenEncMaskGenerator = null; | ||
app._chunkLenDecMaskGenerator = null; | ||
} | ||
function EncodeRequestBody(randomuser, buffer) { | ||
const app = this.app | ||
if (!app._isHeaderSent) { | ||
app._isHeaderSent = true; | ||
const header = EncodeRequestHeader(app, randomuser) | ||
app.user.bytesRead += buffer.length | ||
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.message(Buffer.concat([header, ...chunks])) | ||
} else { | ||
app.user.bytesRead += buffer.length | ||
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
this.message(Buffer.concat(chunks)) | ||
} | ||
if (net.isIPv6(host)) { | ||
return ATYP_V6; | ||
} | ||
return ATYP_DOMAIN; | ||
} | ||
function EncodeRequestHeader(app, cmd) { | ||
function EncodeRequestHeader(app, randomuser) { | ||
const rands = crypto.randomBytes(33); | ||
const [isAEAD, random_user, authInfo, ts] = randomuser(); | ||
app._isAEADRequest = isAEAD | ||
app.user = random_user | ||
// IV and Key for data chunks encryption/decryption | ||
app._dataEncIV = rands.slice(0, 16); | ||
app._dataEncKey = rands.slice(16, 32); | ||
app._dataDecIV = hash('md5', app._dataEncIV); | ||
app._dataDecKey = hash('md5', app._dataEncKey); | ||
app._dataEncIV = rands.subarray(0, 16); | ||
app._dataEncKey = rands.subarray(16, 32); | ||
app._dataEncKeyForChaCha20 = createChacha20Poly1305Key(app._dataEncKey); | ||
app._dataDecKeyForChaCha20 = createChacha20Poly1305Key(app._dataDecKey); | ||
if (isAEAD) { | ||
app._dataDecIV = common.hash('sha256', app._dataEncIV).subarray(0, 16); | ||
app._dataDecKey = common.hash('sha256', app._dataEncKey).subarray(0, 16); | ||
} else { | ||
app._dataDecIV = common.hash('md5', app._dataEncIV); | ||
app._dataDecKey = common.hash('md5', app._dataEncKey); | ||
} | ||
app._chunkLenEncMaskGenerator = shake128(app._dataEncIV); | ||
app._chunkLenDecMaskGenerator = shake128(app._dataDecIV); | ||
app._dataEncKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataEncKey); | ||
app._dataDecKeyForChaCha20 = common.createChacha20Poly1305Key(app._dataDecKey); | ||
app._chunkLenEncMaskGenerator = common.shake128(app._dataEncIV); | ||
app._chunkLenDecMaskGenerator = common.shake128(app._dataDecIV); | ||
app._v = rands[32]; | ||
app._opt = 0x05; | ||
app._option = 0x05; | ||
const uuid = app._uuid; | ||
const { userHashCache } = app.getStore(); | ||
const paddingLen = getRandomInt(0, 15); | ||
const padding = crypto.randomBytes(paddingLen); | ||
const { timestamp, authInfo } = userHashCache[getRandomInt(0, userHashCache.length - 1)]; | ||
// utc timestamp: Big-Endian, 8 bytes | ||
const ts = uint64ToBuffer(timestamp); | ||
app._security = app.user.security || 2 | ||
const paddingLen = getRandomInt(0, 15); | ||
const padding = crypto.randomBytes(paddingLen); | ||
// create encrypted command | ||
let command = Buffer.from([ | ||
0x01, // Ver | ||
...app._dataEncIV, ...app._dataEncKey, app._v, app._opt, | ||
...app._dataEncIV, ...app._dataEncKey, app._v, app._option, | ||
paddingLen << 4 | app._security, | ||
0x00, // RSV | ||
cmd, // Cmd | ||
...app._port, app._atyp, | ||
app._cmd, // Cmd | ||
...app._port, | ||
app._atyp, | ||
...Buffer.concat([ | ||
(app._atyp === ATYP_DOMAIN) ? ntb(app._host.length, 1) : Buffer.alloc(0), | ||
(app._atyp === ATYP_DOMAIN) ? common.ntb(app._host.length, 1) : Buffer.alloc(0), | ||
app._host, | ||
@@ -87,91 +145,77 @@ ]), | ||
]); | ||
command = Buffer.concat([command, fnv1a(command)]); | ||
const cipher = crypto.createCipheriv( | ||
'aes-128-cfb', | ||
hash('md5', Buffer.concat([uuid, Buffer.from('c48619fe-8f02-49e0-b9e9-edf763e17e21')])), | ||
hash('md5', Buffer.concat([ts, ts, ts, ts])), | ||
); | ||
command = cipher.update(command); | ||
return Buffer.concat([authInfo, command]); | ||
} | ||
command = Buffer.concat([command, common.fnv1a(command)]); | ||
function onclose() { | ||
if (this.app.remote) | ||
this.app.remote.close() | ||
this.app._isConnecting = false; | ||
this.app._isHeaderSent = false; | ||
this.app._isHeaderRecv = false; | ||
this.app._adBuf.clear(); | ||
this.app._adBuf = null; | ||
this.app._host = null; | ||
this.app._port = null; | ||
this.app._staging = null; | ||
this.app._dataEncKey = null; | ||
this.app._dataEncKeyForChaCha20 = null; | ||
this.app._dataEncIV = null; | ||
this.app._dataDecKey = null; | ||
this.app._dataDecKeyForChaCha20 = null; | ||
this.app._dataDecIV = null; | ||
this.app._chunkLenEncMaskGenerator = null; | ||
this.app._chunkLenDecMaskGenerator = null; | ||
} | ||
// onInitTargetAddress({ host, port }) { | ||
// const type = getAddrType(host); | ||
// this._atyp = type; | ||
// this._port = ntb(port); | ||
// this._host = (type === ATYP_DOMAIN) ? Buffer.from(host) : ip.toBuffer(host); | ||
// } | ||
function EncodeRequestBody(buffer) { | ||
const app = this.app | ||
app.user.bytesRead += buffer.length | ||
if (!app._isHeaderSent) { | ||
app._isHeaderSent = true; | ||
this.write(EncodeRequestHeader(app)) | ||
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
for (const iterator of chunks) { | ||
this.write(iterator) | ||
if (isAEAD == false) { | ||
const cipher = crypto.createCipheriv( | ||
'aes-128-cfb', | ||
random_user.id.cmdKey, | ||
common.hash('md5', Buffer.concat([ts, ts, ts, ts])), | ||
); | ||
command = cipher.update(command); | ||
return Buffer.concat([authInfo, command]); | ||
} else if (typeof random_user == "object") { | ||
var cipher = validator.SealVMessAEADHeader(random_user.id.cmdKey, command) | ||
if (cipher == undefined) { | ||
return log("AEAD read failed") | ||
} | ||
return cipher; | ||
} else { | ||
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
for (const iterator of chunks) { | ||
this.write(iterator) | ||
} | ||
return log(`invalid user`); | ||
} | ||
} | ||
function DecodeResponseHeader(app) { | ||
var outBuffer = Buffer.from([app._responseHeader, 0x00, 0x00, 0x00]) | ||
if (!app._isAEADRequest) { | ||
var encryptionWriter = crypto.createCipheriv('aes-128-cfb', app._dataEncKey, app._dataEncIV); | ||
return encryptionWriter.update(outBuffer); | ||
} else { | ||
const aeadResponseHeaderLengthEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderLenKey) | ||
const aeadResponseHeaderLengthEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12) | ||
const aeadResponseHeaderLengthEncryptionBuffer = Buffer.allocUnsafe(2); | ||
aeadResponseHeaderLengthEncryptionBuffer.writeUInt16BE(outBuffer.length) | ||
const cipher = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderLengthEncryptionKey, aeadResponseHeaderLengthEncryptionIV); | ||
var AEADEncryptedLength = Buffer.concat([cipher.update(aeadResponseHeaderLengthEncryptionBuffer), cipher.final(), cipher.getAuthTag()]) | ||
function DecodeResponseHeader(buffer) { | ||
const app = this | ||
if (!app._isHeaderRecv) { | ||
if (app._dataDecKey == null) | ||
return | ||
try { | ||
if (!app._isAEADRequest) { | ||
const decipher = crypto.createDecipheriv('aes-128-cfb', app._dataDecKey, app._dataDecIV); | ||
var header = decipher.update(buffer.subarray(0, 4)); | ||
} else { | ||
const aeadResponseHeaderLengthEncryptionKey = kdf.KDF16(app._dataDecKey, consts.KDFSaltConstAEADRespHeaderLenKey) | ||
const aeadResponseHeaderLengthEncryptionIV = kdf.KDF(app._dataDecIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12) | ||
const decipher = crypto.createDecipheriv('aes-128-gcm', aeadResponseHeaderLengthEncryptionKey, aeadResponseHeaderLengthEncryptionIV); | ||
const aeadEncryptedResponseHeaderLength = buffer.subarray(0, 2) | ||
const aeadEncryptedResponseHeaderLengthTag = buffer.subarray(2, 18) | ||
const aeadResponseHeaderPayloadEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderPayloadKey) | ||
const aeadResponseHeaderPayloadEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12) | ||
decipher.setAuthTag(aeadEncryptedResponseHeaderLengthTag); | ||
const decryptedAEADHeaderLengthPayloadResult = Buffer.concat([decipher.update(aeadEncryptedResponseHeaderLength), decipher.final()]) | ||
const decryptedResponseHeaderLength = decryptedAEADHeaderLengthPayloadResult.readUInt16BE(0) + 18 | ||
const cipher2 = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderPayloadEncryptionKey, aeadResponseHeaderPayloadEncryptionIV); | ||
const aeadResponseHeaderPayloadEncryptionKey = kdf.KDF16(app._dataDecKey, consts.KDFSaltConstAEADRespHeaderPayloadKey) | ||
const aeadResponseHeaderPayloadEncryptionIV = kdf.KDF(app._dataDecIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12) | ||
var aeadEncryptedHeaderPayload = Buffer.concat([cipher2.update(outBuffer), cipher2.final(), cipher2.getAuthTag()]) | ||
return Buffer.concat([AEADEncryptedLength, aeadEncryptedHeaderPayload]) | ||
const decipher2 = crypto.createDecipheriv('aes-128-gcm', aeadResponseHeaderPayloadEncryptionKey, aeadResponseHeaderPayloadEncryptionIV); | ||
const encryptedResponseHeaderBuffer = buffer.subarray(18, decryptedResponseHeaderLength) | ||
const encryptedResponseHeaderBufferTag = buffer.subarray(decryptedResponseHeaderLength, decryptedResponseHeaderLength + 16) | ||
decipher2.setAuthTag(encryptedResponseHeaderBufferTag); | ||
var header = Buffer.concat([decipher2.update(encryptedResponseHeaderBuffer), decipher2.final()]) | ||
} | ||
} catch (error) { | ||
return log(error) | ||
} | ||
if (app._v !== header[0]) { | ||
return fail(`server response v doesn't match, expect ${app._v} but got ${header[0]}`); | ||
} | ||
app._isHeaderRecv = true; | ||
return app._adBuf.put(buffer.subarray(4 + header[3]), app); | ||
} | ||
app._adBuf.put(buffer, app); | ||
} | ||
function DecodeResponseBody(chunk, app) { | ||
if ([consts.SECURITY_TYPE_AES_128_GCM, consts.SECURITY_TYPE_CHACHA20_POLY1305].includes(this._security)) { | ||
const tag = chunk.slice(-16); | ||
const data = decrypt.call(app, chunk.slice(2, -16), tag); | ||
if ([consts.SECURITY_TYPE_AES_128_GCM, consts.SECURITY_TYPE_CHACHA20_POLY1305].includes(app._security)) { | ||
const data = common.decrypt(chunk.subarray(2), app); | ||
if (data === null) { | ||
@@ -181,25 +225,40 @@ return log(`fail to verify data chunk, `, chunk + ""); | ||
app.user.bytesWrit += data.length | ||
if (app.remoteWrite) | ||
return app.remoteWrite(data); | ||
if (app.localMessage) | ||
return app.localMessage(data); | ||
} | ||
app.user.bytesWrit += chunk.length - 2 | ||
if (app.remoteWrite) | ||
return app.remoteWrite(chunk.slice(2)); | ||
if (app.localMessage) | ||
return app.localMessage(chunk.subarray(2)); | ||
} | ||
// ================================================= sender | ||
// ================================================= functions | ||
function resolveChunk(chunk) { | ||
console.log(100000); | ||
let _chunk = chunk; | ||
if ([consts.SECURITY_TYPE_AES_128_GCM, consts.SECURITY_TYPE_CHACHA20_POLY1305].includes(this._security)) { | ||
_chunk = Buffer.concat(encrypt(_chunk)); | ||
_chunk = common.encrypt(_chunk, this); | ||
} | ||
let _len = _chunk.length; | ||
if (this._option & 4 !== 0) { | ||
if (this._option >= 0x05) { | ||
const mask = this._chunkLenEncMaskGenerator.nextBytes(2).readUInt16BE(0); | ||
_len = mask ^ _len; | ||
} | ||
return Buffer.concat([ntb(_len), _chunk]); | ||
console.log(10, this._option, _len) | ||
return Buffer.concat([common.ntb(_len), _chunk]); | ||
} | ||
function onReceivingLength(buffer, app) { | ||
if (buffer.length < 2) { | ||
return; | ||
} | ||
let len = buffer.readUInt16BE(0); | ||
if (app._option >= 0x05) { | ||
const mask = app._chunkLenDecMaskGenerator.nextBytes(2).readUInt16BE(0); | ||
len = mask ^ len; | ||
} | ||
console.log(20,app._option, len) | ||
return 2 + len; | ||
} | ||
function getChunks(buffer, maxSize) { | ||
@@ -210,7 +269,7 @@ const totalLen = buffer.length; | ||
while (ptr < totalLen - 1) { | ||
bufs.push(buffer.slice(ptr, ptr + maxSize)); | ||
bufs.push(buffer.subarray(ptr, ptr + maxSize)); | ||
ptr += maxSize; | ||
} | ||
if (ptr < totalLen) { | ||
bufs.push(buffer.slice(ptr)); | ||
bufs.push(buffer.subarray(ptr)); | ||
} | ||
@@ -220,14 +279,78 @@ return bufs; | ||
function onReceivingLength(buffer) { | ||
if (buffer.length < 2) { | ||
return; | ||
const ipv4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/; | ||
const ipv6Regex = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i; | ||
function toBuffer(ip, buff, offset) { | ||
offset = ~~offset; | ||
let result; | ||
if (ipv4Regex.test(ip)) { | ||
result = buff || Buffer.alloc(offset + 4); | ||
ip.split(/\./g).map((byte) => { | ||
result[offset++] = parseInt(byte, 10) & 0xff; | ||
}); | ||
} else if (ipv6Regex.test(ip)) { | ||
const sections = ip.split(':', 8); | ||
let i; | ||
for (i = 0; i < sections.length; i++) { | ||
const isv4 = ipv4Regex.test(sections[i]); | ||
let v4Buffer; | ||
if (isv4) { | ||
v4Buffer = this.toBuffer(sections[i]); | ||
sections[i] = v4Buffer.subarray(0, 2).toString('hex'); | ||
} | ||
if (v4Buffer && ++i < 8) { | ||
sections.splice(i, 0, v4Buffer.subarray(2, 4).toString('hex')); | ||
} | ||
} | ||
if (sections[0] === '') { | ||
while (sections.length < 8) sections.unshift('0'); | ||
} else if (sections[sections.length - 1] === '') { | ||
while (sections.length < 8) sections.push('0'); | ||
} else if (sections.length < 8) { | ||
for (i = 0; i < sections.length && sections[i] !== ''; i++); | ||
const argv = [i, 1]; | ||
for (i = 9 - sections.length; i > 0; i--) { | ||
argv.push('0'); | ||
} | ||
sections.splice(...argv); | ||
} | ||
result = buff || Buffer.alloc(offset + 16); | ||
for (i = 0; i < sections.length; i++) { | ||
const word = parseInt(sections[i], 16); | ||
result[offset++] = (word >> 8) & 0xff; | ||
result[offset++] = word & 0xff; | ||
} | ||
} | ||
let len = buffer.readUInt16BE(0); | ||
if (this._option & 4 !== 0) { | ||
const mask = this._chunkLenDecMaskGenerator.nextBytes(2).readUInt16BE(0); | ||
len = mask ^ len; | ||
if (!result) { | ||
throw Error(`Invalid ip address: ${ip}`); | ||
} | ||
return 2 + len; | ||
return result; | ||
}; | ||
function getAddrType(host) { | ||
if (net.isIPv4(host)) { | ||
return ATYP_V4; | ||
} | ||
if (net.isIPv6(host)) { | ||
return ATYP_V6; | ||
} | ||
return ATYP_DOMAIN; | ||
} | ||
function getRandomInt(min, max) { | ||
min = Math.ceil(min); | ||
max = Math.ceil(max); | ||
const random = crypto.randomBytes(1)[0] / (0xff + 1e-13); | ||
return Math.floor(random * (max - min + 1) + min); | ||
} | ||
module.exports = init |
@@ -5,5 +5,4 @@ | ||
server: require('./server'), | ||
// client: require('./client'), | ||
client: {}, | ||
client: require('./client'), | ||
user: account.user | ||
} |
const common = require("./common") | ||
const validator = require("./validator") | ||
const AdvancedBuffer = require("./advanced-buffer") | ||
const jsSha = require("./crypto/sha3"); | ||
const kdf = require("./kdf") | ||
@@ -16,3 +16,3 @@ const event = require("../../event") | ||
function init(users, remoteProtocol) { | ||
var checkuser = validator.init(users) | ||
var checkuser = validator.init(users).check | ||
return function (socket, ip) { | ||
@@ -29,4 +29,4 @@ socket.app = {} | ||
socket.app._adBuf = new AdvancedBuffer({ getPacketLength: onReceivingLength.bind(socket.app) }); | ||
socket.app._adBuf.on('data', DecodeRequestBody.bind(socket.app)); | ||
socket.app._adBuf = new AdvancedBuffer({ getPacketLength: onReceivingLength }); | ||
socket.app._adBuf.on('data', DecodeRequestBody); | ||
@@ -42,2 +42,3 @@ return { | ||
function DecodeRequestHeader(remoteProtocol, onRemoteMessage, onRemoteClose, checkuser, buffer) { | ||
const app = this.app | ||
@@ -63,4 +64,4 @@ if (!app._isHeaderRecv) { | ||
aeadUser = user[0] | ||
var iv = hash('md5', Buffer.concat([ts, ts, ts, ts])) | ||
var decipher = crypto.createDecipheriv('aes-128-cfb', aeadUser.id.cmdKey, iv); | ||
var decipher = crypto.createDecipheriv('aes-128-cfb', aeadUser.id.cmdKey, | ||
common.hash('md5', Buffer.concat([ts, ts, ts, ts]))); | ||
decipher.subarray = function (a, b) { | ||
@@ -104,17 +105,13 @@ return this.update(reqCommand.subarray(a, b)) | ||
if (app._isAEADRequest) { | ||
app._dataEncIV = hash('sha256', app._dataDecIV).subarray(0, 16); | ||
app._dataEncKey = hash('sha256', app._dataDecKey).subarray(0, 16); | ||
app._dataEncIV = common.hash('sha256', app._dataDecIV).subarray(0, 16); | ||
app._dataEncKey = common.hash('sha256', app._dataDecKey).subarray(0, 16); | ||
} else { | ||
app._dataEncIV = hash('md5', app._dataDecIV); | ||
app._dataEncKey = hash('md5', app._dataDecKey); | ||
app._dataEncIV = common.hash('md5', app._dataDecIV); | ||
app._dataEncKey = common.hash('md5', app._dataDecKey); | ||
} | ||
app._dataDecKeyForChaCha20 = createChacha20Poly1305Key(app._dataDecKey); | ||
app._dataEncKeyForChaCha20 = createChacha20Poly1305Key(app._dataEncKey); | ||
app._chunkLenDecMaskGenerator = shake128(app._dataDecIV); | ||
app._chunkLenEncMaskGenerator = shake128(app._dataEncIV); | ||
app._chunkLenDecMaskGenerator = common.shake128(app._dataDecIV); | ||
app._chunkLenEncMaskGenerator = common.shake128(app._dataEncIV); | ||
app._responseHeader = reqHeader[33]; | ||
app._option = reqHeader[34]; | ||
const paddingLen = reqHeader[35] >> 4; | ||
// 2 'auto' | ||
@@ -179,3 +176,3 @@ // 3 'aes-128-gcm' | ||
if (fnv1a(plainReqHeader).equals(f)) { | ||
if (common.fnv1a(plainReqHeader).equals(f)) { | ||
return log('fail to verify request command'); | ||
@@ -186,3 +183,2 @@ } | ||
app._isConnecting = true; | ||
app.remote = remoteProtocol( | ||
@@ -209,9 +205,8 @@ addrType === ATYP_DOMAIN ? addr.toString() : iptoString(addr), | ||
} | ||
function DecodeRequestBody(chunk) { | ||
var app = this | ||
function DecodeRequestBody(chunk, app) { | ||
if ([consts.SECURITY_TYPE_AES_128_GCM, consts.SECURITY_TYPE_CHACHA20_POLY1305].includes(app._security)) { | ||
const tag = chunk.slice(-16); | ||
const data = decrypt.call(app, chunk.slice(2, -16), tag); | ||
const data = common.decrypt(chunk.subarray(2), app); | ||
if (data === null) { | ||
return log(`fail to verify data chunk, `, chunk + ""); | ||
return log("fail to verify data chunk"); | ||
} | ||
@@ -224,3 +219,3 @@ app.user.bytesWrit += data.length | ||
if (app.remote) | ||
return app.remote.message(chunk.slice(2)); | ||
return app.remote.message(chunk.subarray(2)); | ||
} | ||
@@ -230,24 +225,28 @@ | ||
var outBuffer = Buffer.from([app._responseHeader, 0x00, 0x00, 0x00]) | ||
if (!app._isAEADRequest) { | ||
var encryptionWriter = crypto.createCipheriv('aes-128-cfb', app._dataEncKey, app._dataEncIV); | ||
return encryptionWriter.update(outBuffer); | ||
} else { | ||
const aeadResponseHeaderLengthEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderLenKey) | ||
const aeadResponseHeaderLengthEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12) | ||
const aeadResponseHeaderLengthEncryptionBuffer = Buffer.allocUnsafe(2); | ||
aeadResponseHeaderLengthEncryptionBuffer.writeUInt16BE(outBuffer.length) | ||
try { | ||
if (!app._isAEADRequest) { | ||
var encryptionWriter = crypto.createCipheriv('aes-128-cfb', app._dataEncKey, app._dataEncIV); | ||
return encryptionWriter.update(outBuffer); | ||
} else { | ||
const cipher = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderLengthEncryptionKey, aeadResponseHeaderLengthEncryptionIV); | ||
var AEADEncryptedLength = Buffer.concat([cipher.update(aeadResponseHeaderLengthEncryptionBuffer), cipher.final(), cipher.getAuthTag()]) | ||
const aeadResponseHeaderLengthEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderLenKey) | ||
const aeadResponseHeaderLengthEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderLenIV).subarray(0, 12) | ||
const aeadResponseHeaderLengthEncryptionBuffer = Buffer.alloc(2); | ||
aeadResponseHeaderLengthEncryptionBuffer.writeUInt16BE(outBuffer.length) | ||
const cipher = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderLengthEncryptionKey, aeadResponseHeaderLengthEncryptionIV); | ||
var AEADEncryptedLength = Buffer.concat([cipher.update(aeadResponseHeaderLengthEncryptionBuffer), cipher.final(), cipher.getAuthTag()]) | ||
const aeadResponseHeaderPayloadEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderPayloadKey) | ||
const aeadResponseHeaderPayloadEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12) | ||
const aeadResponseHeaderPayloadEncryptionKey = kdf.KDF16(app._dataEncKey, consts.KDFSaltConstAEADRespHeaderPayloadKey) | ||
const aeadResponseHeaderPayloadEncryptionIV = kdf.KDF(app._dataEncIV, consts.KDFSaltConstAEADRespHeaderPayloadIV).subarray(0, 12) | ||
const cipher2 = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderPayloadEncryptionKey, aeadResponseHeaderPayloadEncryptionIV); | ||
const cipher2 = crypto.createCipheriv('aes-128-gcm', aeadResponseHeaderPayloadEncryptionKey, aeadResponseHeaderPayloadEncryptionIV); | ||
var aeadEncryptedHeaderPayload = Buffer.concat([cipher2.update(outBuffer), cipher2.final(), cipher2.getAuthTag()]) | ||
return Buffer.concat([AEADEncryptedLength, aeadEncryptedHeaderPayload]) | ||
var aeadEncryptedHeaderPayload = Buffer.concat([cipher2.update(outBuffer), cipher2.final(), cipher2.getAuthTag()]) | ||
return Buffer.concat([AEADEncryptedLength, aeadEncryptedHeaderPayload]) | ||
} | ||
} catch (error) { | ||
log(error) | ||
} | ||
@@ -261,16 +260,39 @@ } | ||
app._isHeaderSent = true; | ||
this.localMessage(EncodeResponseHeader(app)) | ||
const header = EncodeResponseHeader(app) | ||
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
for (const iterator of chunks) { | ||
this.localMessage(iterator) | ||
} | ||
this.localMessage(Buffer.concat([header, ...chunks])) | ||
} else { | ||
const chunks = getChunks(buffer, 0x3fff).map(resolveChunk.bind(app)); | ||
for (const iterator of chunks) { | ||
this.localMessage(iterator) | ||
} | ||
this.localMessage(Buffer.concat(chunks)) | ||
} | ||
} | ||
function resolveChunk(chunk) { | ||
let _chunk = chunk; | ||
if ([consts.SECURITY_TYPE_AES_128_GCM, consts.SECURITY_TYPE_CHACHA20_POLY1305].includes(this._security)) { | ||
_chunk = common.encrypt(_chunk, this) | ||
} | ||
let _len = _chunk.length; | ||
if (this._option >= 0x05) { | ||
const mask = this._chunkLenEncMaskGenerator.nextBytes(2).readUInt16BE(0); | ||
_len = mask ^ _len; | ||
} | ||
console.log(10, this._option, _len) | ||
return Buffer.concat([common.ntb(_len), _chunk]); | ||
} | ||
function onReceivingLength(buffer, app) { | ||
if (buffer.length < 2) { | ||
return; | ||
} | ||
let len = buffer.readUInt16BE(0); | ||
if (app._option >= 0x05) { | ||
var mask = app._chunkLenDecMaskGenerator.nextBytes(2).readUInt16BE(0); | ||
len = mask ^ len; | ||
} | ||
console.log(20, app._option, len) | ||
return 2 + len; | ||
} | ||
function onclose() { | ||
@@ -300,15 +322,2 @@ if (this.app.remote) | ||
// ================================================= sender | ||
function resolveChunk(chunk) { | ||
let _chunk = chunk; | ||
if ([consts.SECURITY_TYPE_AES_128_GCM, consts.SECURITY_TYPE_CHACHA20_POLY1305].includes(this._security)) { | ||
_chunk = Buffer.concat(encrypt(_chunk)); | ||
} | ||
let _len = _chunk.length; | ||
if (this._option & 4 !== 0) { | ||
const mask = this._chunkLenEncMaskGenerator.nextBytes(2).readUInt16BE(0); | ||
_len = mask ^ _len; | ||
} | ||
return Buffer.concat([ntb(_len), _chunk]); | ||
} | ||
function getChunks(buffer, maxSize) { | ||
@@ -319,7 +328,7 @@ const totalLen = buffer.length; | ||
while (ptr < totalLen - 1) { | ||
bufs.push(buffer.slice(ptr, ptr + maxSize)); | ||
bufs.push(buffer.subarray(ptr, ptr + maxSize)); | ||
ptr += maxSize; | ||
} | ||
if (ptr < totalLen) { | ||
bufs.push(buffer.slice(ptr)); | ||
bufs.push(buffer.subarray(ptr)); | ||
} | ||
@@ -387,125 +396,4 @@ return bufs; | ||
function encrypt(plaintext) { | ||
const security = this._security; | ||
const nonce = Buffer.concat([ntb(this._cipherNonce), this._dataEncIV.slice(2, 12)]); | ||
let ciphertext = null; | ||
let tag = null; | ||
if (security === consts.SECURITY_TYPE_AES_128_GCM) { | ||
const cipher = crypto.createCipheriv('aes-128-gcm', this._dataEncKey, nonce); | ||
ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); | ||
tag = cipher.getAuthTag(); | ||
} | ||
else if (security === consts.SECURITY_TYPE_CHACHA20_POLY1305) { | ||
const noop = Buffer.alloc(0); | ||
// eslint-disable-next-line | ||
const result = libsodium.crypto_aead_chacha20poly1305_ietf_encrypt_detached( | ||
plaintext, noop, noop, nonce, this._dataEncKeyForChaCha20, | ||
); | ||
ciphertext = Buffer.from(result.ciphertext); | ||
tag = Buffer.from(result.mac); | ||
} | ||
this._cipherNonce += 1; | ||
return [ciphertext, tag]; | ||
} | ||
function decrypt(ciphertext, tag) { | ||
const security = this._security; | ||
const nonce = Buffer.concat([ntb(10), this._dataDecIV.slice(2, 12)]); | ||
console.log(nonce) | ||
if (security === consts.SECURITY_TYPE_AES_128_GCM) { | ||
const decipher = crypto.createDecipheriv('aes-128-gcm', this._dataDecKey, nonce); | ||
decipher.setAuthTag(tag); | ||
try { | ||
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]); | ||
this._decipherNonce += 1; | ||
return plaintext; | ||
} catch (err) { | ||
console.log(err) | ||
return null; | ||
} | ||
} | ||
else if (security === consts.SECURITY_TYPE_CHACHA20_POLY1305) { | ||
const noop = Buffer.alloc(0); | ||
try { | ||
// eslint-disable-next-line | ||
const plaintext = libsodium.crypto_aead_chacha20poly1305_ietf_decrypt_detached( | ||
noop, ciphertext, tag, noop, nonce, this._dataDecKeyForChaCha20, | ||
); | ||
this._decipherNonce += 1; | ||
return Buffer.from(plaintext); | ||
} catch (err) { | ||
return null; | ||
} | ||
} | ||
} | ||
function onReceivingLength(buffer) { | ||
if (buffer.length < 2) { | ||
return; | ||
} | ||
let len = buffer.readUInt16BE(0); | ||
if (this._option & 4 !== 0) { | ||
const mask = this._chunkLenDecMaskGenerator.nextBytes(2).readUInt16BE(0); | ||
len = mask ^ len; | ||
} | ||
return 2 + len; | ||
} | ||
function ntb(num, len = 2, byteOrder = 0) { | ||
if (len < 1) { | ||
throw Error('len must be greater than 0'); | ||
} | ||
const buf = Buffer.alloc(len); | ||
if (byteOrder === 0) { | ||
buf.writeUIntBE(num, 0, len); | ||
} else { | ||
buf.writeUIntLE(num, 0, len); | ||
} | ||
return buf; | ||
} | ||
function hash(algorithm, buffer) { | ||
const hs = crypto.createHash(algorithm); | ||
hs.update(buffer); | ||
return hs.digest(); | ||
} | ||
function createChacha20Poly1305Key(key) { | ||
const md5Key = hash('md5', key); | ||
return Buffer.concat([md5Key, hash('md5', md5Key)]); | ||
} | ||
function shake128(buffer) { | ||
let buffered = []; | ||
let iter = 0; | ||
return { | ||
nextBytes: function nextBytes(n) { | ||
if (iter + n > buffered.length) { | ||
const hash = jsSha.shake128.create(buffered.length * 8 + 512); | ||
hash.update(buffer); | ||
buffered = Buffer.from(hash.arrayBuffer()); | ||
} | ||
const bytes = buffered.subarray(iter, iter + n); | ||
iter += n; | ||
return bytes; | ||
} | ||
}; | ||
} | ||
function fnv1a(buffer) { | ||
let hash = 0x811c9dc5; | ||
for (let i = 0; i < buffer.length; ++i) { | ||
hash ^= buffer[i]; | ||
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); | ||
} | ||
const buf = Buffer.alloc(4); | ||
buf.writeUIntBE(hash >>> 0, 0, 4); | ||
return buf; | ||
} | ||
module.exports = init |
const crypto = require("crypto"), | ||
long = require("./crypto/long"), | ||
common = require("./common"), | ||
_accont = require("./account"), | ||
@@ -17,3 +17,3 @@ aead = require('./aead'), | ||
for (const user of _accont.AsAccount(users_config)) { | ||
for (const user of _accont.AsAccount(users_config.tag, users_config.users)) { | ||
if (user.alterIDs.length == 0) { | ||
@@ -43,3 +43,3 @@ aead.AddUser(AuthIDDecoderHolder, user.id.cmdKey, user) | ||
function NewTimedUserValidator(AuthIDDecoderHolder, users) { | ||
var tuv = { | ||
const tuv = { | ||
users: users, | ||
@@ -56,8 +56,24 @@ userHash: {}, | ||
} | ||
return function (authInfo, isaead) { | ||
if (isaead) | ||
return aead.Match(tuv.aeadDecoderHolder, authInfo) | ||
else if (authInfo in tuv.userHash) { | ||
const cacheItem = tuv.userHash[authInfo] | ||
return [cacheItem.user.user, uint64ToBuffer(cacheItem.timeInc + tuv.baseTime)] | ||
return { | ||
get: function () { | ||
const keys = Object.keys(tuv.aeadDecoderHolder.decoders) | ||
if (keys.length != 0) { | ||
const key = keys[Math.floor(Math.random() * keys.length)] | ||
const cacheItem = tuv.aeadDecoderHolder.decoders[key] | ||
return [true, cacheItem.ticket] | ||
// return [true, cacheItem.ticket, Buffer.from(key, "hex")] | ||
} else { | ||
const keys = Object.keys(tuv.userHash) | ||
const key = keys[Math.floor(Math.random() * keys.length)] | ||
const cacheItem = tuv.userHash[key] | ||
return [false, cacheItem.user.user, Buffer.from(key, "hex"), common.uint64ToBuffer(cacheItem.timeInc + tuv.baseTime)] | ||
} | ||
}, | ||
check: function (authInfo, isaead) { | ||
if (isaead) | ||
return aead.Match(tuv.aeadDecoderHolder, authInfo) | ||
else if (authInfo in tuv.userHash) { | ||
const cacheItem = tuv.userHash[authInfo] | ||
return [cacheItem.user.user, common.uint64ToBuffer(cacheItem.timeInc + tuv.baseTime)] | ||
} | ||
} | ||
@@ -76,3 +92,3 @@ } | ||
for (var ts = genBeginSec; ts <= genEndSec; ts++) { | ||
var hashValue = hasher('md5', id.UUID).update(uint64ToBuffer(ts)).digest("hex"); | ||
var hashValue = hasher('md5', id.UUID).update(common.uint64ToBuffer(ts)).digest("hex"); | ||
v.userHash[hashValue] = { | ||
@@ -116,6 +132,2 @@ user: user, | ||
function uint64ToBuffer(uint64, byteOrder = false /* BE */) { | ||
const numbers = long.fromNumber(uint64, true).toBytes(byteOrder); | ||
return Buffer.from(numbers); | ||
} | ||
@@ -150,3 +162,4 @@ function hasher(alg, key) { | ||
Close, | ||
OpenVMessAEADHeader: aead.OpenVMessAEADHeader | ||
OpenVMessAEADHeader: aead.OpenVMessAEADHeader, | ||
SealVMessAEADHeader: aead.SealVMessAEADHeader | ||
} |
@@ -8,2 +8,3 @@ | ||
} | ||
function remoteNetwork(network) { | ||
@@ -10,0 +11,0 @@ if (network == "tcp") { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
0
6
114373
4
2912
+ Addedlibsodium-wrappers@^0.7.11
+ Addedlibsodium@0.7.15(transitive)
+ Addedlibsodium-wrappers@0.7.15(transitive)