From 722f5e716fb9801a1329dd754268d1199ad97957 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 10 Jan 2024 16:12:54 +0330 Subject: [PATCH] [feature] wireguard inbound Co-Authored-By: Alireza Ahmadi --- web/assets/js/model/xray.js | 125 ++++++++++----- web/assets/js/util/utils.js | 187 ++++++++++++++++++++++ web/html/xui/form/inbound.html | 5 + web/html/xui/form/protocol/wireguard.html | 56 +++++++ web/translation/translate.en_US.toml | 8 + web/translation/translate.es_ES.toml | 8 + web/translation/translate.fa_IR.toml | 8 + web/translation/translate.ru_RU.toml | 8 + web/translation/translate.vi_VN.toml | 8 + web/translation/translate.zh_Hans.toml | 8 + 10 files changed, 377 insertions(+), 44 deletions(-) create mode 100644 web/html/xui/form/protocol/wireguard.html diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index de0b31f1..3492db6f 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -6,6 +6,7 @@ const Protocols = { DOKODEMO: 'dokodemo-door', SOCKS: 'socks', HTTP: 'http', + WIREGUARD: 'wireguard', }; const SSMethods = { @@ -765,16 +766,18 @@ class RealityStreamSettings extends XrayCommonClass { } RealityStreamSettings.Settings = class extends XrayCommonClass { - constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, spiderX= '/') { + constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '', spiderX= '/') { super(); this.publicKey = publicKey; this.fingerprint = fingerprint; + this.serverName = serverName; this.spiderX = spiderX; } static fromJson(json = {}) { return new RealityStreamSettings.Settings( json.publicKey, json.fingerprint, + json.serverName, json.spiderX, ); } @@ -782,6 +785,7 @@ RealityStreamSettings.Settings = class extends XrayCommonClass { return { publicKey: this.publicKey, fingerprint: this.fingerprint, + serverName: this.serverName, spiderX: this.spiderX, }; } @@ -795,6 +799,7 @@ class SockoptStreamSettings extends XrayCommonClass { this.mark = mark; this.tproxy = tproxy; } + static fromJson(json = {}) { if (Object.keys(json).length === 0) return undefined; return new SockoptStreamSettings( @@ -996,42 +1001,6 @@ class Inbound extends XrayCommonClass { } } - get tls() { - return this.stream.security === 'tls'; - } - - set tls(isTls) { - if (isTls) { - this.stream.security = 'tls'; - } else { - this.stream.security = 'none'; - } - } - - get xtls() { - return this.stream.security === 'xtls'; - } - - set xtls(isXtls) { - if (isXtls) { - this.stream.security = 'xtls'; - } else { - this.stream.security = 'none'; - } - } - - get reality() { - return this.stream.security === 'reality'; - } - - set reality(isReality) { - if (isReality) { - this.stream.security = 'reality'; - } else { - this.stream.security = 'none'; - } - } - get network() { return this.stream.network; } @@ -1143,11 +1112,6 @@ class Inbound extends XrayCommonClass { return ["tcp", "ws", "http", "quic", "grpc"].includes(this.network); } - canEnableReality() { - if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false; - return ["tcp", "http", "grpc"].includes(this.network); - } - //this is used for xtls-rprx-vision canEnableTlsFlow() { if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) { @@ -1156,6 +1120,11 @@ class Inbound extends XrayCommonClass { return false; } + canEnableReality() { + if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false; + return ["tcp", "http", "grpc"].includes(this.network); + } + canEnableXtls() { if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false; return this.network === "tcp"; @@ -1608,7 +1577,7 @@ class Inbound extends XrayCommonClass { }); return links.join('\r\n'); } else { - if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, remark); + if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, 'same', remark); return ''; } } @@ -1658,7 +1627,8 @@ Inbound.Settings = class extends XrayCommonClass { case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol); case Protocols.SOCKS: return new Inbound.SocksSettings(protocol); - case Protocols.HTTP: return new Inbound.HttpSettings(protocol); + case Protocols.HTTP: return new Inbound.HttpSettings(protocol); + case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol); default: return null; } } @@ -1672,6 +1642,7 @@ Inbound.Settings = class extends XrayCommonClass { case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json); case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json); case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); + case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json); default: return null; } } @@ -2274,3 +2245,69 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { return new Inbound.HttpSettings.HttpAccount(json.user, json.pass); } }; + +Inbound.WireguardSettings = class extends XrayCommonClass { + constructor(protocol, mtu=1420, secretKey=Wireguard.generateKeypair().privateKey, peers=[new Inbound.WireguardSettings.Peer()], kernelMode=false) { + super(protocol); + this.mtu = mtu; + this.secretKey = secretKey; + this.pubKey = secretKey.length>0 ? Wireguard.generateKeypair(secretKey).publicKey : ''; + this.peers = peers; + this.kernelMode = kernelMode; + } + + addPeer() { + this.peers.push(new Inbound.WireguardSettings.Peer()); + } + + delPeer(index) { + this.peers.splice(index, 1); + } + + static fromJson(json={}){ + return new Inbound.WireguardSettings( + Protocols.WIREGUARD, + json.mtu, + json.secretKey, + json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)), + json.kernelMode, + ); + } + + toJson() { + return { + mtu: this.mtu?? undefined, + secretKey: this.secretKey, + peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers), + kernelMode: this.kernelMode, + }; + } +}; + +Inbound.WireguardSettings.Peer = class extends XrayCommonClass { + constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], keepAlive=0) { + super(); + this.publicKey = publicKey; + this.psk = psk; + this.allowedIPs = allowedIPs; + this.keepAlive = keepAlive; + } + + static fromJson(json={}){ + return new Inbound.WireguardSettings.Peer( + json.publicKey, + json.preSharedKey, + json.allowedIPs, + json.keepAlive + ); + } + + toJson() { + return { + publicKey: this.publicKey, + preSharedKey: this.psk.length>0 ? this.psk : undefined, + allowedIPs: this.allowedIPs, + keepAlive: this.keepAlive?? undefined, + }; + } +}; \ No newline at end of file diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js index 8bab58ec..61b322bd 100644 --- a/web/assets/js/util/utils.js +++ b/web/assets/js/util/utils.js @@ -295,3 +295,190 @@ class ObjectUtil { return true; } } + +class Wireguard { + static gf(init) { + var r = new Float64Array(16); + if (init) { + for (var i = 0; i < init.length; ++i) + r[i] = init[i]; + } + return r; + } + + static pack(o, n) { + var b, m = this.gf(), t = this.gf(); + for (var i = 0; i < 16; ++i) + t[i] = n[i]; + this.carry(t); + this.carry(t); + this.carry(t); + for (var j = 0; j < 2; ++j) { + m[0] = t[0] - 0xffed; + for (var i = 1; i < 15; ++i) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + this.cswap(t, m, 1 - b); + } + for (var i = 0; i < 16; ++i) { + o[2 * i] = t[i] & 0xff; + o[2 * i + 1] = t[i] >> 8; + } + } + + static carry(o) { + var c; + for (var i = 0; i < 16; ++i) { + o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536); + o[i] &= 0xffff; + } + } + + static cswap(p, q, b) { + var t, c = ~(b - 1); + for (var i = 0; i < 16; ++i) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } + } + + static add(o, a, b) { + for (var i = 0; i < 16; ++i) + o[i] = (a[i] + b[i]) | 0; + } + + static subtract(o, a, b) { + for (var i = 0; i < 16; ++i) + o[i] = (a[i] - b[i]) | 0; + } + + static multmod(o, a, b) { + var t = new Float64Array(31); + for (var i = 0; i < 16; ++i) { + for (var j = 0; j < 16; ++j) + t[i + j] += a[i] * b[j]; + } + for (var i = 0; i < 15; ++i) + t[i] += 38 * t[i + 16]; + for (var i = 0; i < 16; ++i) + o[i] = t[i]; + this.carry(o); + this.carry(o); + } + + static invert(o, i) { + var c = this.gf(); + for (var a = 0; a < 16; ++a) + c[a] = i[a]; + for (var a = 253; a >= 0; --a) { + this.multmod(c, c, c); + if (a !== 2 && a !== 4) + this.multmod(c, c, i); + } + for (var a = 0; a < 16; ++a) + o[a] = c[a]; + } + + static clamp(z) { + z[31] = (z[31] & 127) | 64; + z[0] &= 248; + } + + static generatePublicKey(privateKey) { + var r, z = new Uint8Array(32); + var a = this.gf([1]), + b = this.gf([9]), + c = this.gf(), + d = this.gf([1]), + e = this.gf(), + f = this.gf(), + _121665 = this.gf([0xdb41, 1]), + _9 = this.gf([9]); + for (var i = 0; i < 32; ++i) + z[i] = privateKey[i]; + this.clamp(z); + for (var i = 254; i >= 0; --i) { + r = (z[i >>> 3] >>> (i & 7)) & 1; + this.cswap(a, b, r); + this.cswap(c, d, r); + this.add(e, a, c); + this.subtract(a, a, c); + this.add(c, b, d); + this.subtract(b, b, d); + this.multmod(d, e, e); + this.multmod(f, a, a); + this.multmod(a, c, a); + this.multmod(c, b, e); + this.add(e, a, c); + this.subtract(a, a, c); + this.multmod(b, a, a); + this.subtract(c, d, f); + this.multmod(a, c, _121665); + this.add(a, a, d); + this.multmod(c, c, a); + this.multmod(a, d, f); + this.multmod(d, b, _9); + this.multmod(b, e, e); + this.cswap(a, b, r); + this.cswap(c, d, r); + } + this.invert(c, c); + this.multmod(a, a, c); + this.pack(z, a); + return z; + } + + static generatePresharedKey() { + var privateKey = new Uint8Array(32); + window.crypto.getRandomValues(privateKey); + return privateKey; + } + + static generatePrivateKey() { + var privateKey = this.generatePresharedKey(); + this.clamp(privateKey); + return privateKey; + } + + static encodeBase64(dest, src) { + var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]); + for (var i = 0; i < 4; ++i) + dest[i] = input[i] + 65 + + (((25 - input[i]) >> 8) & 6) - + (((51 - input[i]) >> 8) & 75) - + (((61 - input[i]) >> 8) & 15) + + (((62 - input[i]) >> 8) & 3); + } + + static keyToBase64(key) { + var i, base64 = new Uint8Array(44); + for (i = 0; i < 32 / 3; ++i) + this.encodeBase64(base64.subarray(i * 4), key.subarray(i * 3)); + this.encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0])); + base64[43] = 61; + return String.fromCharCode.apply(null, base64); + } + + static keyFromBase64(encoded) { + const binaryStr = atob(encoded); + const bytes = new Uint8Array(binaryStr.length); + for (let i = 0; i < binaryStr.length; i++) { + bytes[i] = binaryStr.charCodeAt(i); + } + return bytes; + } + + static generateKeypair(secretKey='') { + var privateKey = secretKey.length>0 ? this.keyFromBase64(secretKey) : this.generatePrivateKey(); + var publicKey = this.generatePublicKey(privateKey); + return { + publicKey: this.keyToBase64(publicKey), + privateKey: secretKey.length>0 ? secretKey : this.keyToBase64(privateKey) + }; + } +} \ No newline at end of file diff --git a/web/html/xui/form/inbound.html b/web/html/xui/form/inbound.html index 5da8ec56..b19be9ac 100644 --- a/web/html/xui/form/inbound.html +++ b/web/html/xui/form/inbound.html @@ -97,6 +97,11 @@ {{template "form/http"}} + + +