Update On Thu Aug 21 20:41:19 CEST 2025

This commit is contained in:
github-action[bot]
2025-08-21 20:41:19 +02:00
parent ca42e1b16e
commit 7d88eac7d4
76 changed files with 1126 additions and 678 deletions

1
.github/update.log vendored
View File

@@ -1096,3 +1096,4 @@ Update On Sun Aug 17 20:39:03 CEST 2025
Update On Mon Aug 18 20:42:30 CEST 2025
Update On Tue Aug 19 20:36:45 CEST 2025
Update On Wed Aug 20 20:53:01 CEST 2025
Update On Thu Aug 21 20:41:11 CEST 2025

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

View File

@@ -149,6 +149,10 @@ func (c *Conn) ReaderReplaceable() bool {
return c.disablePipe.Load() || c.deadline.Load().IsZero()
}
func (c *Conn) WriterReplaceable() bool {
return true
}
func (c *Conn) Upstream() any {
return c.ExtendedConn
}

View File

@@ -1,97 +0,0 @@
// Copy from https://github.com/WireGuard/wgctrl-go/blob/a9ab2273dd1075ea74b88c76f8757f8b4003fcbf/wgtypes/types.go#L71-L155
package generater
import (
"crypto/rand"
"encoding/base64"
"fmt"
"golang.org/x/crypto/curve25519"
)
// KeyLen is the expected key length for a WireGuard key.
const KeyLen = 32 // wgh.KeyLen
// A Key is a public, private, or pre-shared secret key. The Key constructor
// functions in this package can be used to create Keys suitable for each of
// these applications.
type Key [KeyLen]byte
// GenerateKey generates a Key suitable for use as a pre-shared secret key from
// a cryptographically safe source.
//
// The output Key should not be used as a private key; use GeneratePrivateKey
// instead.
func GenerateKey() (Key, error) {
b := make([]byte, KeyLen)
if _, err := rand.Read(b); err != nil {
return Key{}, fmt.Errorf("wgtypes: failed to read random bytes: %v", err)
}
return NewKey(b)
}
// GeneratePrivateKey generates a Key suitable for use as a private key from a
// cryptographically safe source.
func GeneratePrivateKey() (Key, error) {
key, err := GenerateKey()
if err != nil {
return Key{}, err
}
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
key[0] &= 248
key[31] &= 127
key[31] |= 64
return key, nil
}
// NewKey creates a Key from an existing byte slice. The byte slice must be
// exactly 32 bytes in length.
func NewKey(b []byte) (Key, error) {
if len(b) != KeyLen {
return Key{}, fmt.Errorf("wgtypes: incorrect key size: %d", len(b))
}
var k Key
copy(k[:], b)
return k, nil
}
// ParseKey parses a Key from a base64-encoded string, as produced by the
// Key.String method.
func ParseKey(s string) (Key, error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return Key{}, fmt.Errorf("wgtypes: failed to parse base64-encoded key: %v", err)
}
return NewKey(b)
}
// PublicKey computes a public key from the private key k.
//
// PublicKey should only be called when k is a private key.
func (k Key) PublicKey() Key {
var (
pub [KeyLen]byte
priv = [KeyLen]byte(k)
)
// ScalarBaseMult uses the correct base value per https://cr.yp.to/ecdh.html,
// so no need to specify it.
curve25519.ScalarBaseMult(&pub, &priv)
return Key(pub)
}
// String returns the base64-encoded string representation of a Key.
//
// ParseKey can be used to produce a new Key from this string.
func (k Key) String() string {
return base64.StdEncoding.EncodeToString(k[:])
}

View File

@@ -1,4 +1,4 @@
package generater
package generator
import (
"encoding/base64"
@@ -12,7 +12,7 @@ import (
func Main(args []string) {
if len(args) < 1 {
panic("Using: generate uuid/reality-keypair/wg-keypair/ech-keypair/vless-mlkem768")
panic("Using: generate uuid/reality-keypair/wg-keypair/ech-keypair/vless-mlkem768/vless-x25519")
}
switch args[0] {
case "uuid":
@@ -22,20 +22,19 @@ func Main(args []string) {
}
fmt.Println(newUUID.String())
case "reality-keypair":
privateKey, err := GeneratePrivateKey()
privateKey, err := GenX25519PrivateKey()
if err != nil {
panic(err)
}
publicKey := privateKey.PublicKey()
fmt.Println("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey[:]))
fmt.Println("PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey[:]))
fmt.Println("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey.Bytes()))
fmt.Println("PublicKey: " + base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes()))
case "wg-keypair":
privateKey, err := GeneratePrivateKey()
privateKey, err := GenX25519PrivateKey()
if err != nil {
panic(err)
}
fmt.Println("PrivateKey: " + privateKey.String())
fmt.Println("PublicKey: " + privateKey.PublicKey().String())
fmt.Println("PrivateKey: " + base64.StdEncoding.EncodeToString(privateKey.Bytes()))
fmt.Println("PublicKey: " + base64.StdEncoding.EncodeToString(privateKey.PublicKey().Bytes()))
case "ech-keypair":
if len(args) < 2 {
panic("Using: generate ech-keypair <plain_server_name>")
@@ -51,11 +50,23 @@ func Main(args []string) {
if len(args) > 1 {
seed = args[1]
}
seedBase64, clientBase64, err := encryption.GenMLKEM768(seed)
seedBase64, clientBase64, hash11Base64, err := encryption.GenMLKEM768(seed)
if err != nil {
panic(err)
}
fmt.Println("Seed: " + seedBase64)
fmt.Println("Client: " + clientBase64)
fmt.Println("Hash11: " + hash11Base64)
case "vless-x25519":
var privateKey string
if len(args) > 1 {
privateKey = args[1]
}
privateKeyBase64, passwordBase64, err := encryption.GenX25519(privateKey)
if err != nil {
panic(err)
}
fmt.Println("PrivateKey: " + privateKeyBase64)
fmt.Println("Password: " + passwordBase64)
}
}

View File

@@ -0,0 +1,27 @@
package generator
import (
"crypto/ecdh"
"crypto/rand"
)
const X25519KeySize = 32
func GenX25519PrivateKey() (*ecdh.PrivateKey, error) {
var privateKey [X25519KeySize]byte
_, err := rand.Read(privateKey[:])
if err != nil {
return nil, err
}
// Avoid generating equivalent X25519 private keys
// https://github.com/XTLS/Xray-core/pull/1747
//
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
privateKey[0] &= 248
privateKey[31] &= 127
privateKey[31] |= 64
return ecdh.X25519().NewPrivateKey(privateKey[:])
}

View File

@@ -638,8 +638,12 @@ proxies: # socks5
port: 443
uuid: uuid
network: tcp
encryption: "8min-vless-mlkem768client-bas64RawURLEncoding" # 复用八分钟后协商新的 sharedKey需小于服务端的值
# encryption: "8min-xored-mlkem768client-bas64RawURLEncoding"
# -------------------------
# vless encryption客户端配置:
# (只使用 1-RTT 模式 / 复用八分钟后协商新的 baseKey周期需小于服务端的值
# / 是只能选一个,后面是 base64RawURLEncoding使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
# -------------------------
encryption: "1rtt/8min.native/divide/random.mlkem768Client.(X25519 Password).(ML-KEM-768 Client)"
tls: false #可以不开启tls
udp: true
@@ -1359,8 +1363,12 @@ listeners:
flow: xtls-rprx-vision
# ws-path: "/" # 如果不为空则开启 websocket 传输层
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
# decryption: "10min-vless-mlkem768seed-bas64RawURLEncoding" # 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式, 后面base64字符串可由可由 mihomo generate vless-mlkem768 命令生成
# decryption: "10min-xored-mlkem768seed-bas64RawURLEncoding"
# -------------------------
# vless encryption服务端配置
# (只允许 1-RTT 模式 / 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式;原生外观 / ECH 式 XOR / 全随机数)
# / 是只能选一个,后面是 base64RawURLEncoding使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
# -------------------------
# decryption: "1rtt/10min.native/divide/random.mlkem768Seed.(X25519 PrivateKey).(ML-KEM-768 Seed)"
# 下面两项如果填写则开启 tls需要同时填写
# certificate: ./server.crt
# private-key: ./server.key

View File

@@ -31,7 +31,7 @@ require (
github.com/metacubex/sing-shadowsocks2 v0.2.6
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.7
github.com/metacubex/sing-vmess v0.2.4-0.20250819151326-51d195aac5db
github.com/metacubex/sing-vmess v0.2.4-0.20250821024956-97839f31a65d
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4

View File

@@ -131,8 +131,8 @@ github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MY
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU=
github.com/metacubex/sing-vmess v0.2.4-0.20250819151326-51d195aac5db h1:W7VKxR0r5IR+56Lblx2iyrEaykx0esdQwTQbkSrSaek=
github.com/metacubex/sing-vmess v0.2.4-0.20250819151326-51d195aac5db/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-vmess v0.2.4-0.20250821024956-97839f31a65d h1:jchYEho5+kTmok4aTMflqJyTRnqVPTOVeC1RFXxuw9A=
github.com/metacubex/sing-vmess v0.2.4-0.20250821024956-97839f31a65d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo=

View File

@@ -21,7 +21,7 @@ import (
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/component/generater"
"github.com/metacubex/mihomo/component/generator"
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
@@ -48,13 +48,12 @@ var echConfigBase64, echKeyPem, _ = ech.GenECHConfig(echPublicSni)
func init() {
rand.Read(httpData)
privateKey, err := generater.GeneratePrivateKey()
privateKey, err := generator.GenX25519PrivateKey()
if err != nil {
panic(err)
}
publicKey := privateKey.PublicKey()
realityPrivateKey = base64.RawURLEncoding.EncodeToString(privateKey[:])
realityPublickey = base64.RawURLEncoding.EncodeToString(publicKey[:])
realityPrivateKey = base64.RawURLEncoding.EncodeToString(privateKey.Bytes())
realityPublickey = base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes())
}
type TestTunnel struct {

View File

@@ -89,17 +89,29 @@ func TestInboundVless_TLS(t *testing.T) {
}
func TestInboundVless_Encryption(t *testing.T) {
seedBase64, clientBase64, err := encryption.GenMLKEM768("")
seedBase64, clientBase64, _, err := encryption.GenMLKEM768("")
if err != nil {
t.Fatal(err)
return
}
t.Run("-vless-", func(t *testing.T) {
privateKeyBase64, passwordBase64, err := encryption.GenX25519("")
if err != nil {
t.Fatal(err)
return
}
var modes = []string{
"native",
"divide",
"random",
}
for i := range modes {
mode := modes[i]
t.Run(mode, func(t *testing.T) {
inboundOptions := inbound.VlessOption{
Decryption: "10min-vless-mlkem768seed-" + seedBase64,
Decryption: "10min." + mode + ".mlkem768Seed." + privateKeyBase64 + "." + seedBase64,
}
outboundOptions := outbound.VlessOption{
Encryption: "8min-vless-mlkem768client-" + clientBase64,
Encryption: "8min." + mode + ".mlkem768Client." + passwordBase64 + "." + clientBase64,
}
testInboundVless(t, inboundOptions, outboundOptions)
t.Run("xtls-rprx-vision", func(t *testing.T) {
@@ -108,20 +120,7 @@ func TestInboundVless_Encryption(t *testing.T) {
testInboundVless(t, inboundOptions, outboundOptions)
})
})
t.Run("-xored-", func(t *testing.T) {
inboundOptions := inbound.VlessOption{
Decryption: "10min-xored-mlkem768seed-" + seedBase64,
}
outboundOptions := outbound.VlessOption{
Encryption: "8min-xored-mlkem768client-" + clientBase64,
}
testInboundVless(t, inboundOptions, outboundOptions)
t.Run("xtls-rprx-vision", func(t *testing.T) {
outboundOptions := outboundOptions
outboundOptions.Flow = "xtls-rprx-vision"
testInboundVless(t, inboundOptions, outboundOptions)
})
})
}
func TestInboundVless_Wss1(t *testing.T) {

View File

@@ -14,7 +14,7 @@ import (
"strings"
"syscall"
"github.com/metacubex/mihomo/component/generater"
"github.com/metacubex/mihomo/component/generator"
"github.com/metacubex/mihomo/component/geodata"
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/config"
@@ -73,7 +73,7 @@ func main() {
}
if len(os.Args) > 1 && os.Args[1] == "generate" {
generater.Main(os.Args[2:])
generator.Main(os.Args[2:])
return
}

View File

@@ -3,6 +3,7 @@ package encryption
import (
"bytes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"errors"
"fmt"
@@ -40,7 +41,8 @@ type ClientInstance struct {
sync.RWMutex
nfsEKey *mlkem.EncapsulationKey768
hash11 [11]byte // no more capacity
xorKey []byte
xorMode uint32
xorPKey *ecdh.PublicKey
minutes time.Duration
expire time.Time
baseKey []byte
@@ -60,22 +62,23 @@ type ClientConn struct {
input bytes.Reader // peerCache
}
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
func (i *ClientInstance) Init(nfsEKeyBytes, xorPKeyBytes []byte, xorMode, minutes uint32) (err error) {
if i.nfsEKey != nil {
err = errors.New("already initialized")
return
}
i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
if err != nil {
if i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes); err != nil {
return
}
hash32 := sha3.Sum256(nfsEKeyBytes)
copy(i.hash11[:], hash32[:])
if xor > 0 {
xorKey := sha3.Sum256(nfsEKeyBytes)
i.xorKey = xorKey[:]
if xorMode > 0 {
i.xorMode = xorMode
if i.xorPKey, err = ecdh.X25519().NewPublicKey(xorPKeyBytes); err != nil {
return
}
i.minutes = minutes
}
i.minutes = time.Duration(minutes) * time.Minute
return
}
@@ -83,8 +86,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
if i.nfsEKey == nil {
return nil, errors.New("uninitialized")
}
if i.xorKey != nil {
conn = NewXorConn(conn, i.xorKey)
if i.xorMode > 0 {
conn, _ = NewXorConn(conn, i.xorMode, i.xorPKey, nil)
}
c := &ClientConn{Conn: conn}
@@ -145,7 +148,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
}
c.baseKey = append(pfsKey, nfsKey...)
VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
VLESS, _ := NewAEAD(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
if !bytes.Equal(VLESS, []byte("VLESS")) {
return nil, errors.New("invalid server")
}
@@ -180,7 +183,7 @@ func (c *ClientConn) Write(b []byte) (int, error) {
rand.Read(c.random)
copy(data[5+32:], c.random)
EncodeHeader(data[5+32+32:], 23, len(b)+16)
c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket)
c.aead = NewAEAD(ClientCipher, c.baseKey, c.random, c.ticket)
c.nonce = make([]byte, 12)
c.aead.Seal(data[:5+32+32+5], c.nonce, b, data[5+32+32:5+32+32+5])
} else {
@@ -188,7 +191,7 @@ func (c *ClientConn) Write(b []byte) (int, error) {
EncodeHeader(data, 23, len(b)+16)
c.aead.Seal(data[:5], c.nonce, b, data[:5])
if bytes.Equal(c.nonce, MaxNonce) {
c.aead = NewAead(ClientCipher, c.baseKey, data[5:], data[:5])
c.aead = NewAEAD(ClientCipher, c.baseKey, data[5:], data[:5])
}
}
IncreaseNonce(c.nonce)
@@ -229,7 +232,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
if c.random == nil {
return 0, errors.New("empty c.random")
}
c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandomHello, c.random)
c.peerAead = NewAEAD(ClientCipher, c.baseKey, peerRandomHello, c.random)
c.peerNonce = make([]byte, 12)
}
if c.input.Len() > 0 {
@@ -252,7 +255,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
}
var peerAead cipher.AEAD
if bytes.Equal(c.peerNonce, MaxNonce) {
peerAead = NewAead(ClientCipher, c.baseKey, peerData, h)
peerAead = NewAEAD(ClientCipher, c.baseKey, peerData, h)
}
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
if peerAead != nil {

View File

@@ -73,7 +73,7 @@ func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error)
}
}
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
func NewAEAD(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
key := make([]byte, 32)
hkdf.New(sha3.New256, secret, salt, info).Read(key)
if c&1 == 1 {

View File

@@ -14,4 +14,5 @@
// https://github.com/XTLS/Xray-core/commit/d1fb48521271251a8c74bd64fcc2fc8700717a3b
// https://github.com/XTLS/Xray-core/commit/49580705f6029648399304b816a2737f991582a8
// https://github.com/XTLS/Xray-core/commit/84835bec7d0d8555d0dd30953ed26a272de814c4
// https://github.com/XTLS/Xray-core/commit/373558ed7abdbac3de41745cf30ec04c9adde604
package encryption

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"strconv"
"strings"
"time"
)
// NewClient new client from encryption string
@@ -15,7 +14,7 @@ func NewClient(encryption string) (*ClientInstance, error) {
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
return nil, nil
}
if s := strings.SplitN(encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
if s := strings.Split(encryption, "."); len(s) == 5 && s[2] == "mlkem768Client" {
var minutes uint32
if s[0] != "1rtt" {
t := strings.TrimSuffix(s[0], "min")
@@ -28,27 +27,35 @@ func NewClient(encryption string) (*ClientInstance, error) {
}
minutes = uint32(i)
}
var xor uint32
var xorMode uint32
switch s[1] {
case "vless":
case "xored":
xor = 1
case "native":
case "divide":
xorMode = 1
case "random":
xorMode = 2
default:
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
b, err := base64.RawURLEncoding.DecodeString(s[3])
xorPKeyBytes, err := base64.RawURLEncoding.DecodeString(s[3])
if err != nil {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
if len(b) == MLKEM768ClientLength {
if len(xorPKeyBytes) != X25519PasswordSize {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
nfsEKeyBytes, err := base64.RawURLEncoding.DecodeString(s[4])
if err != nil {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
if len(nfsEKeyBytes) != MLKEM768ClientLength {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
client := &ClientInstance{}
if err = client.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
if err = client.Init(nfsEKeyBytes, xorPKeyBytes, xorMode, minutes); err != nil {
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
}
return client, nil
} else {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
}
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
@@ -60,7 +67,7 @@ func NewServer(decryption string) (*ServerInstance, error) {
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
return nil, nil
}
if s := strings.SplitN(decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
if s := strings.Split(decryption, "."); len(s) == 5 && s[2] == "mlkem768Seed" {
var minutes uint32
if s[0] != "1rtt" {
t := strings.TrimSuffix(s[0], "min")
@@ -73,27 +80,35 @@ func NewServer(decryption string) (*ServerInstance, error) {
}
minutes = uint32(i)
}
var xor uint32
var xorMode uint32
switch s[1] {
case "vless":
case "xored":
xor = 1
case "native":
case "divide":
xorMode = 1
case "random":
xorMode = 2
default:
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
b, err := base64.RawURLEncoding.DecodeString(s[3])
xorSKeyBytes, err := base64.RawURLEncoding.DecodeString(s[3])
if err != nil {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
if len(b) == MLKEM768SeedLength {
if len(xorSKeyBytes) != X25519PrivateKeySize {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
nfsDKeySeed, err := base64.RawURLEncoding.DecodeString(s[4])
if err != nil {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
if len(nfsDKeySeed) != MLKEM768SeedLength {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
server := &ServerInstance{}
if err = server.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
if err = server.Init(nfsDKeySeed, xorSKeyBytes, xorMode, minutes); err != nil {
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
}
return server, nil
} else {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
}
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}

View File

@@ -1,25 +1,29 @@
package encryption
import (
"crypto/ecdh"
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/metacubex/utls/mlkem"
"golang.org/x/crypto/sha3"
)
const MLKEM768SeedLength = mlkem.SeedSize
const MLKEM768ClientLength = mlkem.EncapsulationKeySize768
const X25519PasswordSize = 32
const X25519PrivateKeySize = 32
func GenMLKEM768(seedStr string) (seedBase64, clientBase64 string, err error) {
var seed [64]byte
func GenMLKEM768(seedStr string) (seedBase64, clientBase64, hash11Base64 string, err error) {
var seed [MLKEM768SeedLength]byte
if len(seedStr) > 0 {
s, _ := base64.RawURLEncoding.DecodeString(seedStr)
if len(s) != 64 {
if len(s) != MLKEM768SeedLength {
err = fmt.Errorf("invalid length of ML-KEM-768 seed: %s", seedStr)
return
}
seed = [64]byte(s)
seed = [MLKEM768SeedLength]byte(s)
} else {
_, err = rand.Read(seed[:])
if err != nil {
@@ -28,8 +32,45 @@ func GenMLKEM768(seedStr string) (seedBase64, clientBase64 string, err error) {
}
key, _ := mlkem.NewDecapsulationKey768(seed[:])
pub := key.EncapsulationKey()
client := key.EncapsulationKey().Bytes()
hash32 := sha3.Sum256(client)
seedBase64 = base64.RawURLEncoding.EncodeToString(seed[:])
clientBase64 = base64.RawURLEncoding.EncodeToString(pub.Bytes())
clientBase64 = base64.RawURLEncoding.EncodeToString(client)
hash11Base64 = base64.RawURLEncoding.EncodeToString(hash32[:11])
return
}
func GenX25519(privateKeyStr string) (privateKeyBase64, passwordBase64 string, err error) {
var privateKey [X25519PrivateKeySize]byte
if len(privateKeyStr) > 0 {
s, _ := base64.RawURLEncoding.DecodeString(privateKeyStr)
if len(s) != X25519PrivateKeySize {
err = fmt.Errorf("invalid length of X25519 private key: %s", privateKeyStr)
return
}
privateKey = [X25519PrivateKeySize]byte(s)
} else {
_, err = rand.Read(privateKey[:])
if err != nil {
return
}
}
// Avoid generating equivalent X25519 private keys
// https://github.com/XTLS/Xray-core/pull/1747
//
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
privateKey[0] &= 248
privateKey[31] &= 127
privateKey[31] |= 64
key, err := ecdh.X25519().NewPrivateKey(privateKey[:])
if err != nil {
fmt.Println(err.Error())
return
}
privateKeyBase64 = base64.RawURLEncoding.EncodeToString(privateKey[:])
passwordBase64 = base64.RawURLEncoding.EncodeToString(key.PublicKey().Bytes())
return
}

View File

@@ -3,6 +3,7 @@ package encryption
import (
"bytes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"errors"
"fmt"
@@ -26,7 +27,8 @@ type ServerInstance struct {
sync.RWMutex
nfsDKey *mlkem.DecapsulationKey768
hash11 [11]byte // no more capacity
xorKey []byte
xorMode uint32
xorSKey *ecdh.PrivateKey
minutes time.Duration
sessions map[[32]byte]*ServerSession
closed bool
@@ -45,23 +47,24 @@ type ServerConn struct {
nonce []byte
}
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) {
func (i *ServerInstance) Init(nfsDKeySeed, xorSKeyBytes []byte, xorMode, minutes uint32) (err error) {
if i.nfsDKey != nil {
err = errors.New("already initialized")
return
}
i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
if err != nil {
if i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed); err != nil {
return
}
hash32 := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
copy(i.hash11[:], hash32[:])
if xor > 0 {
xorKey := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
i.xorKey = xorKey[:]
if xorMode > 0 {
i.xorMode = xorMode
if i.xorSKey, err = ecdh.X25519().NewPrivateKey(xorSKeyBytes); err != nil {
return
}
}
if minutes > 0 {
i.minutes = minutes
i.minutes = time.Duration(minutes) * time.Minute
i.sessions = make(map[[32]byte]*ServerSession)
go func() {
for {
@@ -95,8 +98,11 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*ServerConn, error) {
if i.nfsDKey == nil {
return nil, errors.New("uninitialized")
}
if i.xorKey != nil {
conn = NewXorConn(conn, i.xorKey)
if i.xorMode > 0 {
var err error
if conn, err = NewXorConn(conn, i.xorMode, nil, i.xorSKey); err != nil {
return nil, err
}
}
c := &ServerConn{Conn: conn}
@@ -167,7 +173,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*ServerConn, error) {
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
c.baseKey = append(pfsKey, nfsKey...)
c.ticket = append(i.hash11[:], NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...)
c.ticket = append(i.hash11[:], NewAEAD(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...)
paddingLen := randBetween(100, 1000)
@@ -221,7 +227,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
}
c.peerRandom = peerTicketHello[32:]
}
c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket)
c.peerAead = NewAEAD(c.cipher, c.baseKey, c.peerRandom, c.ticket)
c.peerNonce = make([]byte, 12)
}
if c.input.Len() > 0 {
@@ -244,7 +250,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
}
var peerAead cipher.AEAD
if bytes.Equal(c.peerNonce, MaxNonce) {
peerAead = NewAead(c.cipher, c.baseKey, peerData, h)
peerAead = NewAEAD(c.cipher, c.baseKey, peerData, h)
}
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
if peerAead != nil {
@@ -280,7 +286,7 @@ func (c *ServerConn) Write(b []byte) (int, error) {
EncodeHeader(data, 0, 32)
rand.Read(data[5 : 5+32])
EncodeHeader(data[5+32:], 23, len(b)+16)
c.aead = NewAead(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
c.aead = NewAEAD(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
c.nonce = make([]byte, 12)
c.aead.Seal(data[:5+32+5], c.nonce, b, data[5+32:5+32+5])
} else {
@@ -288,7 +294,7 @@ func (c *ServerConn) Write(b []byte) (int, error) {
EncodeHeader(data, 23, len(b)+16)
c.aead.Seal(data[:5], c.nonce, b, data[:5])
if bytes.Equal(c.nonce, MaxNonce) {
c.aead = NewAead(c.cipher, c.baseKey, data[5:], data[:5])
c.aead = NewAEAD(c.cipher, c.baseKey, data[5:], data[:5])
}
}
IncreaseNonce(c.nonce)

View File

@@ -3,13 +3,21 @@ package encryption
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"errors"
"io"
"net"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
)
type XorConn struct {
net.Conn
Divide bool
head []byte
key []byte
ctr cipher.Stream
peerCtr cipher.Stream
@@ -25,8 +33,55 @@ type XorConn struct {
in_skip int
}
func NewXorConn(conn net.Conn, key []byte) *XorConn {
return &XorConn{Conn: conn, key: key}
func NewCTR(key, iv []byte, isServer bool) cipher.Stream {
info := "CLIENT"
if isServer {
info = "SERVER" // avoids attackers sending traffic back to the client, though the encryption layer has its own protection
}
hkdf.New(sha3.New256, key, iv, []byte(info)).Read(key) // avoids using pKey directly if attackers sent the basepoint, or whaterver they like
block, _ := aes.NewCipher(key)
return cipher.NewCTR(block, iv)
}
func NewXorConn(conn net.Conn, mode uint32, pKey *ecdh.PublicKey, sKey *ecdh.PrivateKey) (*XorConn, error) {
if mode == 0 || (pKey == nil && sKey == nil) || (pKey != nil && sKey != nil) {
return nil, errors.New("invalid parameters")
}
c := &XorConn{
Conn: conn,
Divide: mode == 1,
isHeader: true,
out_header: make([]byte, 0, 5), // important
in_header: make([]byte, 0, 5), // important
}
if pKey != nil {
c.head = make([]byte, 16+32)
rand.Read(c.head)
eSKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
NewCTR(pKey.Bytes(), c.head[:16], false).XORKeyStream(c.head[16:], eSKey.PublicKey().Bytes()) // make X25519 public key distinguishable from random bytes
c.key, _ = eSKey.ECDH(pKey)
c.ctr = NewCTR(c.key, c.head[:16], false)
}
if sKey != nil {
peerHead := make([]byte, 16+32)
if _, err := io.ReadFull(c.Conn, peerHead); err != nil {
return nil, err
}
NewCTR(sKey.PublicKey().Bytes(), peerHead[:16], false).XORKeyStream(peerHead[16:], peerHead[16:]) // we don't use buggy elligator, because we have PSK :)
ePKey, err := ecdh.X25519().NewPublicKey(peerHead[16:])
if err != nil {
return nil, err
}
key, err := sKey.ECDH(ePKey)
if err != nil {
return nil, err
}
c.peerCtr = NewCTR(key, peerHead[:16], false)
c.head = make([]byte, 16)
rand.Read(c.head) // make sure the server always replies random bytes even when received replays, though it is not important
c.ctr = NewCTR(key, c.head, true) // the same key links the upload & download, though the encryption layer has its own link
}
return c, nil
//chacha20.NewUnauthenticatedCipher()
}
@@ -35,13 +90,6 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
return 0, nil
}
if !c.out_after0 {
var iv []byte
if c.ctr == nil {
block, _ := aes.NewCipher(c.key)
iv = make([]byte, 16)
rand.Read(iv)
c.ctr = cipher.NewCTR(block, iv)
}
t, l, _ := DecodeHeader(b)
if t == 23 { // single 23
l = 5
@@ -49,20 +97,24 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
l += 10
if t == 0 {
c.out_after0 = true
c.out_header = make([]byte, 0, 5) // important
if c.Divide {
l -= 5
}
}
}
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
if iv != nil {
b = append(iv, b...)
l = len(b)
if c.head != nil {
b = append(c.head, b...)
c.head = nil
}
if _, err := c.Conn.Write(b); err != nil {
return 0, err
}
if iv != nil {
b = b[16:] // for len(b)
return l, nil
}
return len(b), nil
if c.Divide {
return c.Conn.Write(b)
}
for p := b; ; { // for XTLS
if len(p) <= c.out_skip {
@@ -93,14 +145,12 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
return 0, nil
}
if !c.in_after0 || !c.isHeader {
if c.peerCtr == nil {
if c.peerCtr == nil { // for client
peerIv := make([]byte, 16)
if _, err := io.ReadFull(c.Conn, peerIv); err != nil {
return 0, err
}
block, _ := aes.NewCipher(c.key)
c.peerCtr = cipher.NewCTR(block, peerIv)
c.isHeader = true
c.peerCtr = NewCTR(c.key, peerIv, true)
}
if _, err := io.ReadFull(c.Conn, b); err != nil {
return 0, err
@@ -117,7 +167,6 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
c.isHeader = false
if t == 0 {
c.in_after0 = true
c.in_header = make([]byte, 0, 5) // important
}
}
} else {
@@ -125,6 +174,9 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
}
return len(b), nil
}
if c.Divide {
return c.Conn.Read(b)
}
n, err := c.Conn.Read(b)
for p := b[:n]; ; { // for XTLS
if len(p) <= c.in_skip {
@@ -146,3 +198,27 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
}
return n, err
}
func (c *XorConn) WriterReplaceable() bool {
if !c.Divide { // never replaceable
return false
}
if !c.out_after0 {
return false
}
return true
}
func (c *XorConn) ReaderReplaceable() bool {
if !c.Divide { // never replaceable
return false
}
if !c.in_after0 || !c.isHeader {
return false
}
return true
}
func (c *XorConn) Upstream() any {
return c.Conn
}

View File

@@ -19,7 +19,7 @@
"@mui/icons-material": "7.3.1",
"@mui/lab": "7.0.0-beta.16",
"@mui/material": "7.3.1",
"@mui/x-date-pickers": "8.10.0",
"@mui/x-date-pickers": "8.10.2",
"@nyanpasu/interface": "workspace:^",
"@nyanpasu/ui": "workspace:^",
"@tailwindcss/postcss": "4.1.12",
@@ -78,7 +78,7 @@
"@vitejs/plugin-react-swc": "4.0.1",
"change-case": "5.4.4",
"clsx": "2.1.1",
"core-js": "3.45.0",
"core-js": "3.45.1",
"filesize": "11.0.2",
"meta-json-schema": "1.19.12",
"monaco-yaml": "5.4.0",

View File

@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.12",
"mihomo_alpha": "alpha-2790481",
"mihomo_alpha": "alpha-5f09db2",
"clash_rs": "v0.9.0",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.0-alpha+sha.51d55ef"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2025-08-19T22:21:14.276Z"
"updated_at": "2025-08-20T22:21:19.472Z"
}

View File

@@ -67,8 +67,8 @@
"@types/fs-extra": "11.0.4",
"@types/lodash-es": "4.17.12",
"@types/node": "22.17.2",
"@typescript-eslint/eslint-plugin": "8.39.1",
"@typescript-eslint/parser": "8.39.1",
"@typescript-eslint/eslint-plugin": "8.40.0",
"@typescript-eslint/parser": "8.40.0",
"autoprefixer": "10.4.21",
"conventional-changelog-conventionalcommits": "9.1.0",
"cross-env": "10.0.0",
@@ -107,7 +107,7 @@
"tailwindcss": "4.1.12",
"tsx": "4.20.4",
"typescript": "5.9.2",
"typescript-eslint": "8.39.1"
"typescript-eslint": "8.40.0"
},
"packageManager": "pnpm@10.14.0",
"engines": {

View File

@@ -50,11 +50,11 @@ importers:
specifier: 22.17.2
version: 22.17.2
'@typescript-eslint/eslint-plugin':
specifier: 8.39.1
version: 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
specifier: 8.40.0
version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/parser':
specifier: 8.39.1
version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
specifier: 8.40.0
version: 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
autoprefixer:
specifier: 10.4.21
version: 10.4.21(postcss@8.5.6)
@@ -75,13 +75,13 @@ importers:
version: 10.1.8(eslint@9.33.0(jiti@2.5.1))
eslint-import-resolver-alias:
specifier: 1.1.2
version: 1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))
version: 1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))
eslint-plugin-html:
specifier: 8.1.3
version: 8.1.3
eslint-plugin-import:
specifier: 2.32.0
version: 2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))
version: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-n:
specifier: 17.21.3
version: 17.21.3(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
@@ -111,7 +111,7 @@ importers:
version: 16.1.5
neostandard:
specifier: 0.12.2
version: 0.12.2(@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
version: 0.12.2(@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
npm-run-all2:
specifier: 8.0.4
version: 8.0.4
@@ -170,8 +170,8 @@ importers:
specifier: 5.9.2
version: 5.9.2
typescript-eslint:
specifier: 8.39.1
version: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
specifier: 8.40.0
version: 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
frontend/interface:
dependencies:
@@ -237,8 +237,8 @@ importers:
specifier: 7.3.1
version: 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@mui/x-date-pickers':
specifier: 8.10.0
version: 8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
specifier: 8.10.2
version: 8.10.2(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@nyanpasu/interface':
specifier: workspace:^
version: link:../interface
@@ -286,7 +286,7 @@ importers:
version: 0.4.0
material-react-table:
specifier: npm:@greenhat616/material-react-table@4.0.0
version: '@greenhat616/material-react-table@4.0.0(098023d060c5c1c6a96afb3bdaa051b0)'
version: '@greenhat616/material-react-table@4.0.0(d0e09da0dd57ee89e64ea69924c8dcff)'
monaco-editor:
specifier: 0.52.2
version: 0.52.2
@@ -307,7 +307,7 @@ importers:
version: 1.6.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react-hook-form-mui:
specifier: 7.6.2
version: 7.6.2(aea177882beb7723aeada5c99e57089b)
version: 7.6.2(8d03dcd7938824115d8443966a6d9038)
react-i18next:
specifier: 15.6.1
version: 15.6.1(i18next@25.3.6(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)
@@ -409,8 +409,8 @@ importers:
specifier: 2.1.1
version: 2.1.1
core-js:
specifier: 3.45.0
version: 3.45.0
specifier: 3.45.1
version: 3.45.1
filesize:
specifier: 11.0.2
version: 11.0.2
@@ -1970,8 +1970,8 @@ packages:
'@types/react':
optional: true
'@mui/x-date-pickers@8.10.0':
resolution: {integrity: sha512-3nY+SS2/JtqcptQodECIyWKsTvPBDAcXKkyW65R4rQUCrnV6tuzriSrzy/FEYqTK0hyXYPIGJhQ6A0FbtQ9AkQ==}
'@mui/x-date-pickers@8.10.2':
resolution: {integrity: sha512-eS5t1jUojN/jL2FeJ8gtpCBxIEswUp9kLjM64aJ5LUKrNgM7X9dwsEHyplS+x07kWLiEAhO3nX3mepnS3Z43qg==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@emotion/react': ^11.9.0
@@ -2007,8 +2007,8 @@ packages:
moment-jalaali:
optional: true
'@mui/x-internals@8.10.0':
resolution: {integrity: sha512-stYhWBeCKfV2/ltAWShZ3ZJ51otbqpMpC+krWWoIsxM8TuvGzwXw5YMU9L2fTb8hRstsiOCQfEzIn12Ii7+N0Q==}
'@mui/x-internals@8.10.2':
resolution: {integrity: sha512-dlC0BQRRBdiWtqn1yDppaHYRUjU3OuPWTxy0UtqxDaJjJf4pfR8ALr243nbxgJAFqvQyWPWyO4A6p9x9eJMJEQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -3427,16 +3427,16 @@ packages:
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
'@typescript-eslint/eslint-plugin@8.39.1':
resolution: {integrity: sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==}
'@typescript-eslint/eslint-plugin@8.40.0':
resolution: {integrity: sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.39.1
'@typescript-eslint/parser': ^8.40.0
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/parser@8.39.1':
resolution: {integrity: sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==}
'@typescript-eslint/parser@8.40.0':
resolution: {integrity: sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -3448,8 +3448,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/project-service@8.39.1':
resolution: {integrity: sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==}
'@typescript-eslint/project-service@8.40.0':
resolution: {integrity: sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -3458,8 +3458,8 @@ packages:
resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.39.1':
resolution: {integrity: sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==}
'@typescript-eslint/scope-manager@8.40.0':
resolution: {integrity: sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.38.0':
@@ -3468,20 +3468,20 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/tsconfig-utils@8.39.0':
resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/tsconfig-utils@8.39.1':
resolution: {integrity: sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.39.1':
resolution: {integrity: sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==}
'@typescript-eslint/tsconfig-utils@8.40.0':
resolution: {integrity: sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.40.0':
resolution: {integrity: sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -3495,22 +3495,22 @@ packages:
resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.39.0':
resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.39.1':
resolution: {integrity: sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.40.0':
resolution: {integrity: sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.38.0':
resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/typescript-estree@8.39.1':
resolution: {integrity: sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==}
'@typescript-eslint/typescript-estree@8.40.0':
resolution: {integrity: sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -3522,8 +3522,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/utils@8.39.1':
resolution: {integrity: sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==}
'@typescript-eslint/utils@8.40.0':
resolution: {integrity: sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -3533,8 +3533,8 @@ packages:
resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.39.1':
resolution: {integrity: sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==}
'@typescript-eslint/visitor-keys@8.40.0':
resolution: {integrity: sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.2.0':
@@ -4253,8 +4253,8 @@ packages:
core-js-compat@3.44.0:
resolution: {integrity: sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==}
core-js@3.45.0:
resolution: {integrity: sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==}
core-js@3.45.1:
resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==}
cosmiconfig-typescript-loader@6.1.0:
resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==}
@@ -8133,8 +8133,8 @@ packages:
typedarray-to-buffer@3.1.5:
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
typescript-eslint@8.39.1:
resolution: {integrity: sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==}
typescript-eslint@8.40.0:
resolution: {integrity: sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -8765,7 +8765,7 @@ snapshots:
'@babel/generator@7.17.7':
dependencies:
'@babel/types': 7.28.1
'@babel/types': 7.28.2
jsesc: 2.5.2
source-map: 0.5.7
optional: true
@@ -8856,20 +8856,20 @@ snapshots:
'@babel/helper-environment-visitor@7.24.7':
dependencies:
'@babel/types': 7.28.1
'@babel/types': 7.28.2
optional: true
'@babel/helper-function-name@7.24.7':
dependencies:
'@babel/template': 7.27.2
'@babel/types': 7.28.1
'@babel/types': 7.28.2
optional: true
'@babel/helper-globals@7.28.0': {}
'@babel/helper-hoist-variables@7.24.7':
dependencies:
'@babel/types': 7.28.1
'@babel/types': 7.28.2
optional: true
'@babel/helper-member-expression-to-functions@7.25.9':
@@ -8982,7 +8982,7 @@ snapshots:
'@babel/helper-split-export-declaration@7.24.7':
dependencies:
'@babel/types': 7.28.1
'@babel/types': 7.28.2
optional: true
'@babel/helper-string-parser@7.27.1': {}
@@ -9574,13 +9574,13 @@ snapshots:
'@babel/traverse@7.23.2':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.0
'@babel/generator': 7.28.3
'@babel/helper-environment-visitor': 7.24.7
'@babel/helper-function-name': 7.24.7
'@babel/helper-hoist-variables': 7.24.7
'@babel/helper-split-export-declaration': 7.24.7
'@babel/parser': 7.28.0
'@babel/types': 7.28.1
'@babel/parser': 7.28.3
'@babel/types': 7.28.2
debug: 4.4.1
globals: 11.12.0
transitivePeerDependencies:
@@ -10032,13 +10032,13 @@ snapshots:
'@fastify/busboy@2.1.1': {}
'@greenhat616/material-react-table@4.0.0(098023d060c5c1c6a96afb3bdaa051b0)':
'@greenhat616/material-react-table@4.0.0(d0e09da0dd57ee89e64ea69924c8dcff)':
dependencies:
'@emotion/react': 11.14.0(@types/react@19.1.10)(react@19.1.1)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
'@mui/icons-material': 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@mui/x-date-pickers': 8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@mui/x-date-pickers': 8.10.2(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@tanstack/match-sorter-utils': 8.19.4
'@tanstack/react-table': 8.21.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@tanstack/react-virtual': 3.13.9(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -10286,13 +10286,13 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.10
'@mui/x-date-pickers@8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
'@mui/x-date-pickers@8.10.2(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@babel/runtime': 7.28.2
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@mui/system': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
'@mui/x-internals': 8.10.0(@types/react@19.1.10)(react@19.1.1)
'@mui/x-internals': 8.10.2(@types/react@19.1.10)(react@19.1.1)
'@types/react-transition-group': 4.4.12(@types/react@19.1.10)
clsx: 2.1.1
prop-types: 15.8.1
@@ -10306,7 +10306,7 @@ snapshots:
transitivePeerDependencies:
- '@types/react'
'@mui/x-internals@8.10.0(@types/react@19.1.10)(react@19.1.1)':
'@mui/x-internals@8.10.2(@types/react@19.1.10)(react@19.1.1)':
dependencies:
'@babel/runtime': 7.28.2
'@mui/utils': 7.3.1(@types/react@19.1.10)(react@19.1.1)
@@ -11377,7 +11377,7 @@ snapshots:
'@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.6.2)':
dependencies:
'@babel/generator': 7.17.7
'@babel/parser': 7.28.0
'@babel/parser': 7.28.3
'@babel/traverse': 7.23.2
'@babel/types': 7.17.0
javascript-natural-sort: 0.7.1
@@ -11657,14 +11657,14 @@ snapshots:
'@types/node': 22.17.2
optional: true
'@typescript-eslint/eslint-plugin@8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
'@typescript-eslint/eslint-plugin@8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/scope-manager': 8.39.1
'@typescript-eslint/type-utils': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/utils': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 8.39.1
'@typescript-eslint/parser': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/scope-manager': 8.40.0
'@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 8.40.0
eslint: 9.33.0(jiti@2.5.1)
graphemer: 1.4.0
ignore: 7.0.5
@@ -11674,12 +11674,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
'@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@typescript-eslint/scope-manager': 8.39.1
'@typescript-eslint/types': 8.39.1
'@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 8.39.1
'@typescript-eslint/scope-manager': 8.40.0
'@typescript-eslint/types': 8.40.0
'@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 8.40.0
debug: 4.4.1
eslint: 9.33.0(jiti@2.5.1)
typescript: 5.9.2
@@ -11688,17 +11688,17 @@ snapshots:
'@typescript-eslint/project-service@8.38.0(typescript@5.9.2)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2)
'@typescript-eslint/types': 8.39.0
'@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.9.2)
'@typescript-eslint/types': 8.39.1
debug: 4.4.1
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.39.1(typescript@5.9.2)':
'@typescript-eslint/project-service@8.40.0(typescript@5.9.2)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.9.2)
'@typescript-eslint/types': 8.39.1
'@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2)
'@typescript-eslint/types': 8.40.0
debug: 4.4.1
typescript: 5.9.2
transitivePeerDependencies:
@@ -11709,28 +11709,28 @@ snapshots:
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/visitor-keys': 8.38.0
'@typescript-eslint/scope-manager@8.39.1':
'@typescript-eslint/scope-manager@8.40.0':
dependencies:
'@typescript-eslint/types': 8.39.1
'@typescript-eslint/visitor-keys': 8.39.1
'@typescript-eslint/types': 8.40.0
'@typescript-eslint/visitor-keys': 8.40.0
'@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.9.2)':
dependencies:
typescript: 5.9.2
'@typescript-eslint/tsconfig-utils@8.39.0(typescript@5.9.2)':
dependencies:
typescript: 5.9.2
'@typescript-eslint/tsconfig-utils@8.39.1(typescript@5.9.2)':
dependencies:
typescript: 5.9.2
'@typescript-eslint/type-utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
'@typescript-eslint/tsconfig-utils@8.40.0(typescript@5.9.2)':
dependencies:
'@typescript-eslint/types': 8.39.1
'@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2)
'@typescript-eslint/utils': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
typescript: 5.9.2
'@typescript-eslint/type-utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@typescript-eslint/types': 8.40.0
'@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2)
'@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
debug: 4.4.1
eslint: 9.33.0(jiti@2.5.1)
ts-api-utils: 2.1.0(typescript@5.9.2)
@@ -11742,10 +11742,10 @@ snapshots:
'@typescript-eslint/types@8.38.0': {}
'@typescript-eslint/types@8.39.0': {}
'@typescript-eslint/types@8.39.1': {}
'@typescript-eslint/types@8.40.0': {}
'@typescript-eslint/typescript-estree@8.38.0(typescript@5.9.2)':
dependencies:
'@typescript-eslint/project-service': 8.38.0(typescript@5.9.2)
@@ -11762,12 +11762,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.39.1(typescript@5.9.2)':
'@typescript-eslint/typescript-estree@8.40.0(typescript@5.9.2)':
dependencies:
'@typescript-eslint/project-service': 8.39.1(typescript@5.9.2)
'@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.9.2)
'@typescript-eslint/types': 8.39.1
'@typescript-eslint/visitor-keys': 8.39.1
'@typescript-eslint/project-service': 8.40.0(typescript@5.9.2)
'@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2)
'@typescript-eslint/types': 8.40.0
'@typescript-eslint/visitor-keys': 8.40.0
debug: 4.4.1
fast-glob: 3.3.3
is-glob: 4.0.3
@@ -11789,12 +11789,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
'@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0(jiti@2.5.1))
'@typescript-eslint/scope-manager': 8.39.1
'@typescript-eslint/types': 8.39.1
'@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2)
'@typescript-eslint/scope-manager': 8.40.0
'@typescript-eslint/types': 8.40.0
'@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2)
eslint: 9.33.0(jiti@2.5.1)
typescript: 5.9.2
transitivePeerDependencies:
@@ -11805,9 +11805,9 @@ snapshots:
'@typescript-eslint/types': 8.38.0
eslint-visitor-keys: 4.2.1
'@typescript-eslint/visitor-keys@8.39.1':
'@typescript-eslint/visitor-keys@8.40.0':
dependencies:
'@typescript-eslint/types': 8.39.1
'@typescript-eslint/types': 8.40.0
eslint-visitor-keys: 4.2.1
'@ungap/structured-clone@1.2.0': {}
@@ -11881,7 +11881,7 @@ snapshots:
babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.0)
browserslist: 4.25.1
browserslist-to-esbuild: 2.1.1(browserslist@4.25.1)
core-js: 3.45.0
core-js: 3.45.1
magic-string: 0.30.17
regenerator-runtime: 0.14.1
systemjs: 6.15.1
@@ -12584,7 +12584,7 @@ snapshots:
dependencies:
browserslist: 4.25.1
core-js@3.45.0: {}
core-js@3.45.1: {}
cosmiconfig-typescript-loader@6.1.0(@types/node@22.17.2)(cosmiconfig@9.0.0(typescript@5.9.2))(typescript@5.9.2):
dependencies:
@@ -13397,9 +13397,9 @@ snapshots:
optionalDependencies:
unrs-resolver: 1.10.1
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))):
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))):
dependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))
eslint-import-resolver-node@0.3.9:
dependencies:
@@ -13409,7 +13409,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1)):
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.1
@@ -13420,16 +13420,16 @@ snapshots:
tinyglobby: 0.2.14
unrs-resolver: 1.10.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)):
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/parser': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
eslint: 9.33.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
@@ -13446,7 +13446,7 @@ snapshots:
dependencies:
htmlparser2: 10.0.0
eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)):
eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)):
dependencies:
'@typescript-eslint/types': 8.35.1
comment-parser: 1.4.1
@@ -13459,12 +13459,12 @@ snapshots:
stable-hash-x: 0.2.0
unrs-resolver: 1.10.1
optionalDependencies:
'@typescript-eslint/utils': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -13475,7 +13475,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.33.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1))
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -13487,7 +13487,7 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/parser': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@@ -15286,20 +15286,20 @@ snapshots:
sax: 1.3.0
optional: true
neostandard@0.12.2(@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2):
neostandard@0.12.2(@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2):
dependencies:
'@humanwhocodes/gitignore-to-minimatch': 1.0.2
'@stylistic/eslint-plugin': 2.11.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
eslint: 9.33.0(jiti@2.5.1)
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1))
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1)))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-n: 17.21.3(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
eslint-plugin-promise: 7.2.1(eslint@9.33.0(jiti@2.5.1))
eslint-plugin-react: 7.37.5(eslint@9.33.0(jiti@2.5.1))
find-up: 5.0.0
globals: 15.15.0
peowly: 1.3.2
typescript-eslint: 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
typescript-eslint: 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
transitivePeerDependencies:
- '@typescript-eslint/utils'
- eslint-import-resolver-node
@@ -15847,14 +15847,14 @@ snapshots:
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
react-hook-form-mui@7.6.2(aea177882beb7723aeada5c99e57089b):
react-hook-form-mui@7.6.2(8d03dcd7938824115d8443966a6d9038):
dependencies:
'@mui/material': 7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react: 19.1.1
react-hook-form: 7.52.1(react@19.1.1)
optionalDependencies:
'@mui/icons-material': 7.3.1(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@types/react@19.1.10)(react@19.1.1)
'@mui/x-date-pickers': 8.10.0(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@mui/x-date-pickers': 8.10.2(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@mui/material@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@mui/system@7.3.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(react@19.1.1))(@types/react@19.1.10)(dayjs@1.11.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react-hook-form@7.52.1(react@19.1.1):
dependencies:
@@ -17000,12 +17000,12 @@ snapshots:
dependencies:
is-typedarray: 1.0.0
typescript-eslint@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2):
typescript-eslint@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2):
dependencies:
'@typescript-eslint/eslint-plugin': 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/parser': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2)
'@typescript-eslint/utils': 8.39.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/eslint-plugin': 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/parser': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2)
'@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)
eslint: 9.33.0(jiti@2.5.1)
typescript: 5.9.2
transitivePeerDependencies:

View File

@@ -91,6 +91,12 @@ define U-Boot/Bananapro
BUILD_DEVICES:=lemaker_bananapro
endef
define U-Boot/beelink_x2
BUILD_SUBTARGET:=cortexa7
NAME:=Beelink X2 (H3)
BUILD_DEVICES:=roofull_beelink-x2
endef
define U-Boot/Cubieboard
BUILD_SUBTARGET:=cortexa8
NAME:=Cubieboard
@@ -112,6 +118,7 @@ endef
define U-Boot/Hummingbird_A31
BUILD_SUBTARGET:=cortexa7
NAME:=Hummingbird A31 board
BUILD_DEVICES:=merrii_hummingbird
endef
define U-Boot/Marsboard_A10
@@ -138,6 +145,12 @@ define U-Boot/Linksprite_pcDuino
BUILD_DEVICES:=linksprite_a10-pcduino
endef
define U-Boot/LicheePi_Zero
BUILD_SUBTARGET:=cortexa7
NAME:=Lichee Pi Zero V3s
BUILD_DEVICES:=licheepi_licheepi-zero-dock
endef
define U-Boot/Linksprite_pcDuino3
BUILD_SUBTARGET:=cortexa7
NAME:=Linksprite pcDuino3
@@ -337,6 +350,15 @@ define U-Boot/orangepi_zero2
ATF:=h616
endef
define U-Boot/orangepi_zero2w
BUILD_SUBTARGET:=cortexa53
NAME:=Xunlong Orange Pi Zero2W
BUILD_DEVICES:=xunlong_orangepi-zero2w
DEPENDS:=+PACKAGE_u-boot-orangepi_zero2w:trusted-firmware-a-sunxi-h616
UENV:=h616
ATF:=h616
endef
define U-Boot/orangepi_zero3
BUILD_SUBTARGET:=cortexa53
NAME:=Xunlong Orange Pi Zero3
@@ -380,6 +402,7 @@ UBOOT_TARGETS := \
bananapi_p2_zero \
Bananapi_M2_Ultra \
Bananapro \
beelink_x2 \
Cubieboard \
Cubieboard2 \
Cubietruck \
@@ -387,6 +410,7 @@ UBOOT_TARGETS := \
Marsboard_A10 \
Mele_M9 \
OLIMEX_A13_SOM \
LicheePi_Zero \
Linksprite_pcDuino \
Linksprite_pcDuino3 \
Linksprite_pcDuino3_Nano \
@@ -409,6 +433,7 @@ UBOOT_TARGETS := \
orangepi_2 \
orangepi_pc2 \
orangepi_zero2 \
orangepi_zero2w \
orangepi_zero3 \
pangolin \
pine64_plus \

View File

@@ -1,7 +1,6 @@
setenv mmc_rootpart 2
part uuid mmc ${mmc_bootdev}:${mmc_rootpart} uuid
setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_addr_r uImage
setenv loaddtb fatload mmc \$mmc_bootdev \$fdt_addr_r dtb
setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_comp_addr_r uImage
setenv bootargs console=ttyS0,115200 earlyprintk root=PARTUUID=${uuid} rootwait earlycon=uart,mmio32,0x01c28000
setenv uenvcmd run loadkernel \&\& run loaddtb \&\& booti \$kernel_addr_r - \$fdt_addr_r
setenv uenvcmd run loadkernel \&\& bootm \$kernel_comp_addr_r
run uenvcmd

View File

@@ -1,8 +1,6 @@
setenv fdt_high ffffffff
setenv mmc_rootpart 2
part uuid mmc ${mmc_bootdev}:${mmc_rootpart} uuid
setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_addr_r uImage
setenv loaddtb fatload mmc \$mmc_bootdev \$fdt_addr_r dtb
setenv bootargs console=ttyS0,115200 earlyprintk root=PARTUUID=${uuid} rootwait
setenv uenvcmd run loadkernel \&\& run loaddtb \&\& bootm \$kernel_addr_r - \$fdt_addr_r
setenv uenvcmd run loadkernel \&\& bootm \$kernel_addr_r
run uenvcmd

View File

@@ -1,7 +1,6 @@
setenv mmc_rootpart 2
part uuid mmc ${mmc_bootdev}:${mmc_rootpart} uuid
setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_addr_r uImage
setenv loaddtb fatload mmc \$mmc_bootdev \$fdt_addr_r dtb
setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_comp_addr_r uImage
setenv bootargs console=ttyS0,115200 earlyprintk root=PARTUUID=${uuid} rootwait
setenv uenvcmd run loadkernel \&\& run loaddtb \&\& booti \$kernel_addr_r - \$fdt_addr_r
setenv uenvcmd run loadkernel \&\& bootm \$kernel_comp_addr_r
run uenvcmd

View File

@@ -1,7 +1,6 @@
setenv mmc_rootpart 2
part uuid mmc ${mmc_bootdev}:${mmc_rootpart} uuid
setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_addr_r uImage
setenv loaddtb fatload mmc \$mmc_bootdev \$fdt_addr_r dtb
setenv loadkernel fatload mmc \$mmc_bootdev \$kernel_comp_addr_r uImage
setenv bootargs console=ttyS0,115200 earlyprintk root=PARTUUID=${uuid} rootwait
setenv uenvcmd run loadkernel \&\& run loaddtb \&\& booti \$kernel_addr_r - \$fdt_addr_r
setenv uenvcmd run loadkernel \&\& bootm \$kernel_comp_addr_r
run uenvcmd

View File

@@ -1,6 +1,4 @@
setenv fdt_high ffffffff
setenv loadkernel fatload mmc 0 \$kernel_addr_r uImage
setenv loaddtb fatload mmc 0 \$fdt_addr_r dtb
setenv bootargs console=ttyS2,115200 earlyprintk root=/dev/mmcblk0p2 rootwait
setenv uenvcmd run loadkernel \&\& run loaddtb \&\& bootm \$kernel_addr_r - \$fdt_addr_r
setenv uenvcmd run loadkernel \&\& run loaddtb \&\& bootm \$kernel_addr_r
run uenvcmd

View File

@@ -16,6 +16,7 @@ CONFIG_NET_DSA=y
CONFIG_NET_DSA_TAG_BRCM=y
CONFIG_NET_DSA_TAG_BRCM_COMMON=y
CONFIG_NET_DSA_TAG_BRCM_LEGACY=y
CONFIG_NET_DSA_TAG_BRCM_LEGACY_FCS=y
CONFIG_NET_DSA_TAG_BRCM_PREPEND=y
CONFIG_NET_SWITCHDEV=y
CONFIG_NOP_USB_XCEIV=y

View File

@@ -16,7 +16,6 @@ define Build/sunxi-sdcard
mkfs.fat $@.boot -C $(FAT32_BLOCKS)
mcopy -i $@.boot $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)-boot.scr ::boot.scr
mcopy -i $@.boot $(DTS_DIR)/$(SUNXI_DTS).dtb ::dtb
mcopy -i $@.boot $(IMAGE_KERNEL) ::uImage
./gen_sunxi_sdcard_img.sh $@ \
$@.boot \
@@ -34,10 +33,18 @@ define Device/Default
KERNEL := kernel-bin | uImage none
IMAGES := sdcard.img.gz
IMAGE/sdcard.img.gz := sunxi-sdcard | append-metadata | gzip
SUNXI_DTS_DIR :=allwinner/
SUNXI_DTS_DIR := allwinner/
SUNXI_DTS = $$(SUNXI_DTS_DIR)$$(SOC)-$(lastword $(subst _, ,$(1)))
endef
define Device/FitImageLzma
KERNEL = kernel-bin | lzma | fit lzma $$(DTS_DIR)/$$(SUNXI_DTS).dtb
endef
define Device/FitImageGzip
KERNEL = kernel-bin | gzip | fit gzip $$(DTS_DIR)/$$(SUNXI_DTS).dtb
endef
include $(SUBTARGET).mk
$(eval $(call BuildImage))

View File

@@ -3,12 +3,12 @@
# Copyright (C) 2013-2016 OpenWrt.org
# Copyright (C) 2016 Yousong Zhou
KERNEL_LOADADDR:=0x40008000
KERNEL_LOADADDR:=0x40080000
define Device/sun50i
$(call Device/FitImageLzma)
SUNXI_DTS_DIR := allwinner/
KERNEL_NAME := Image
KERNEL := kernel-bin
endef
define Device/sun50i-a64
@@ -127,6 +127,13 @@ define Device/xunlong_orangepi-zero2
endef
TARGET_DEVICES += xunlong_orangepi-zero2
define Device/xunlong_orangepi-zero2w
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi Zero 2W
$(Device/sun50i-h618)
endef
TARGET_DEVICES += xunlong_orangepi-zero2w
define Device/xunlong_orangepi-zero3
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi Zero 3

View File

@@ -6,6 +6,7 @@
KERNEL_LOADADDR:=0x40008000
define Device/cubietech_cubieboard2
$(call Device/FitImageGzip)
DEVICE_VENDOR := Cubietech
DEVICE_MODEL := Cubieboard2
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-sun4i-emac kmod-rtc-sunxi
@@ -14,6 +15,7 @@ endef
TARGET_DEVICES += cubietech_cubieboard2
define Device/cubietech_cubietruck
$(call Device/FitImageGzip)
DEVICE_VENDOR := Cubietech
DEVICE_MODEL := Cubietruck
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-rtc-sunxi kmod-brcmfmac
@@ -22,6 +24,7 @@ endef
TARGET_DEVICES += cubietech_cubietruck
define Device/friendlyarm_nanopi-m1-plus
$(call Device/FitImageGzip)
DEVICE_VENDOR := FriendlyARM
DEVICE_MODEL := NanoPi M1 Plus
DEVICE_PACKAGES:=kmod-leds-gpio kmod-brcmfmac \
@@ -31,6 +34,7 @@ endef
TARGET_DEVICES += friendlyarm_nanopi-m1-plus
define Device/friendlyarm_nanopi-neo
$(call Device/FitImageGzip)
DEVICE_VENDOR := FriendlyARM
DEVICE_MODEL := NanoPi NEO
SOC := sun8i-h3
@@ -38,6 +42,7 @@ endef
TARGET_DEVICES += friendlyarm_nanopi-neo
define Device/friendlyarm_nanopi-neo-air
$(call Device/FitImageGzip)
DEVICE_VENDOR := FriendlyARM
DEVICE_MODEL := NanoPi NEO Air
DEVICE_PACKAGES := kmod-leds-gpio kmod-brcmfmac \
@@ -47,6 +52,7 @@ endef
TARGET_DEVICES += friendlyarm_nanopi-neo-air
define Device/friendlyarm_nanopi-r1
$(call Device/FitImageGzip)
DEVICE_VENDOR := FriendlyARM
DEVICE_MODEL := NanoPi R1
DEVICE_PACKAGES := kmod-usb-net-rtl8152 kmod-leds-gpio \
@@ -56,6 +62,7 @@ endef
TARGET_DEVICES += friendlyarm_nanopi-r1
define Device/friendlyarm_zeropi
$(call Device/FitImageGzip)
DEVICE_VENDOR := FriendlyARM
DEVICE_MODEL := ZeroPi
DEVICE_PACKAGES := kmod-rtc-sunxi
@@ -64,6 +71,7 @@ endef
TARGET_DEVICES += friendlyarm_zeropi
define Device/lamobo_lamobo-r1
$(call Device/FitImageGzip)
DEVICE_VENDOR := Lamobo
DEVICE_MODEL := Lamobo R1
DEVICE_ALT0_VENDOR := Bananapi
@@ -76,6 +84,7 @@ endef
TARGET_DEVICES += lamobo_lamobo-r1
define Device/lemaker_bananapi
$(call Device/FitImageGzip)
DEVICE_VENDOR := LeMaker
DEVICE_MODEL := Banana Pi
DEVICE_PACKAGES:=kmod-rtc-sunxi kmod-ata-sunxi
@@ -84,6 +93,7 @@ endef
TARGET_DEVICES += lemaker_bananapi
define Device/sinovoip_bananapi-m2-berry
$(call Device/FitImageGzip)
DEVICE_VENDOR := Sinovoip
DEVICE_MODEL := Banana Pi M2 Berry
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-brcmfmac \
@@ -94,6 +104,7 @@ endef
TARGET_DEVICES += sinovoip_bananapi-m2-berry
define Device/sinovoip_bananapi-m2-ultra
$(call Device/FitImageGzip)
DEVICE_VENDOR := Sinovoip
DEVICE_MODEL := Banana Pi M2 Ultra
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-brcmfmac \
@@ -104,6 +115,7 @@ endef
TARGET_DEVICES += sinovoip_bananapi-m2-ultra
define Device/lemaker_bananapro
$(call Device/FitImageGzip)
DEVICE_VENDOR := LeMaker
DEVICE_MODEL := Banana Pro
DEVICE_PACKAGES:=kmod-rtc-sunxi kmod-ata-sunxi kmod-brcmfmac \
@@ -112,7 +124,17 @@ define Device/lemaker_bananapro
endef
TARGET_DEVICES += lemaker_bananapro
define Device/licheepi_licheepi-zero-dock
$(call Device/FitImageGzip)
DEVICE_VENDOR := LicheePi
DEVICE_MODEL := Zero with Dock (V3s)
DEVICE_PACKAGES:=kmod-rtc-sunxi
SOC := sun8i-v3s
endef
TARGET_DEVICES += licheepi_licheepi-zero-dock
define Device/linksprite_pcduino3
$(call Device/FitImageGzip)
DEVICE_VENDOR := LinkSprite
DEVICE_MODEL := pcDuino3
DEVICE_PACKAGES:=kmod-sun4i-emac kmod-rtc-sunxi kmod-ata-sunxi kmod-rtl8xxxu \
@@ -122,6 +144,7 @@ endef
TARGET_DEVICES += linksprite_pcduino3
define Device/linksprite_pcduino3-nano
$(call Device/FitImageGzip)
DEVICE_VENDOR := LinkSprite
DEVICE_MODEL := pcDuino3 Nano
DEVICE_PACKAGES:=kmod-rtc-sunxi kmod-ata-sunxi
@@ -130,6 +153,7 @@ endef
TARGET_DEVICES += linksprite_pcduino3-nano
define Device/mele_m9
$(call Device/FitImageGzip)
DEVICE_VENDOR := Mele
DEVICE_MODEL := M9
DEVICE_PACKAGES:=kmod-sun4i-emac kmod-rtl8192cu
@@ -137,7 +161,17 @@ define Device/mele_m9
endef
TARGET_DEVICES += mele_m9
define Device/merrii_hummingbird
$(call Device/FitImageGzip)
DEVICE_VENDOR := Merrii
DEVICE_MODEL := Hummingbird
DEVICE_PACKAGES:=kmod-brcmfmac cypress-firmware-43362-sdio wpad-basic-mbedtls
SOC := sun6i-a31
endef
TARGET_DEVICES += merrii_hummingbird
define Device/olimex_a20-olinuxino-lime
$(call Device/FitImageGzip)
DEVICE_VENDOR := Olimex
DEVICE_MODEL := A20-OLinuXino-LIME
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-rtc-sunxi
@@ -146,6 +180,7 @@ endef
TARGET_DEVICES += olimex_a20-olinuxino-lime
define Device/olimex_a20-olinuxino-lime2
$(call Device/FitImageGzip)
DEVICE_VENDOR := Olimex
DEVICE_MODEL := A20-OLinuXino-LIME2
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-rtc-sunxi kmod-usb-hid
@@ -154,6 +189,7 @@ endef
TARGET_DEVICES += olimex_a20-olinuxino-lime2
define Device/olimex_a20-olinuxino-lime2-emmc
$(call Device/FitImageGzip)
DEVICE_VENDOR := Olimex
DEVICE_MODEL := A20-OLinuXino-LIME2
DEVICE_VARIANT := eMMC
@@ -163,6 +199,7 @@ endef
TARGET_DEVICES += olimex_a20-olinuxino-lime2-emmc
define Device/olimex_a20-olinuxino-micro
$(call Device/FitImageGzip)
DEVICE_VENDOR := Olimex
DEVICE_MODEL := A20-OLinuXino-MICRO
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-sun4i-emac kmod-rtc-sunxi
@@ -170,7 +207,18 @@ define Device/olimex_a20-olinuxino-micro
endef
TARGET_DEVICES += olimex_a20-olinuxino-micro
define Device/roofull_beelink-x2
$(call Device/FitImageGzip)
DEVICE_VENDOR := Roofull
DEVICE_MODEL := Beelink-X2
DEVICE_PACKAGES:=kmod-leds-gpio kmod-gpio-button-hotplug \
kmod-brcmfmac cypress-firmware-43430-sdio wpad-basic-mbedtls
SOC := sun8i-h3
endef
TARGET_DEVICES += roofull_beelink-x2
define Device/sinovoip_bananapi-m2-plus
$(call Device/FitImageGzip)
DEVICE_VENDOR := Sinovoip
DEVICE_MODEL := Banana Pi M2+
DEVICE_PACKAGES:=kmod-leds-gpio kmod-brcmfmac \
@@ -180,6 +228,7 @@ endef
TARGET_DEVICES += sinovoip_bananapi-m2-plus
define Device/sinovoip_bananapi-m3
$(call Device/FitImageGzip)
DEVICE_VENDOR := Sinovoip
DEVICE_MODEL := Banana Pi M3
DEVICE_PACKAGES:=kmod-rtc-sunxi kmod-leds-gpio kmod-rtc-ac100 \
@@ -189,6 +238,7 @@ endef
TARGET_DEVICES += sinovoip_bananapi-m3
define Device/sinovoip_bananapi-p2-zero
$(call Device/FitImageGzip)
DEVICE_VENDOR := Sinovoip
DEVICE_MODEL := Banana Pi P2 Zero
DEVICE_PACKAGES:=kmod-leds-gpio kmod-brcmfmac \
@@ -198,6 +248,7 @@ endef
TARGET_DEVICES += sinovoip_bananapi-p2-zero
define Device/xunlong_orangepi-one
$(call Device/FitImageGzip)
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi One
DEVICE_PACKAGES:=kmod-rtc-sunxi
@@ -206,6 +257,7 @@ endef
TARGET_DEVICES += xunlong_orangepi-one
define Device/xunlong_orangepi-pc
$(call Device/FitImageGzip)
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi PC
DEVICE_PACKAGES:=kmod-gpio-button-hotplug
@@ -214,6 +266,7 @@ endef
TARGET_DEVICES += xunlong_orangepi-pc
define Device/xunlong_orangepi-pc-plus
$(call Device/FitImageGzip)
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi PC Plus
DEVICE_PACKAGES:=kmod-gpio-button-hotplug
@@ -222,6 +275,7 @@ endef
TARGET_DEVICES += xunlong_orangepi-pc-plus
define Device/xunlong_orangepi-plus
$(call Device/FitImageGzip)
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi Plus
DEVICE_PACKAGES:=kmod-rtc-sunxi
@@ -230,6 +284,7 @@ endef
TARGET_DEVICES += xunlong_orangepi-plus
define Device/xunlong_orangepi-r1
$(call Device/FitImageGzip)
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi R1
DEVICE_PACKAGES:=kmod-usb-net-rtl8152
@@ -238,6 +293,7 @@ endef
TARGET_DEVICES += xunlong_orangepi-r1
define Device/xunlong_orangepi-zero
$(call Device/FitImageGzip)
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi Zero
DEVICE_PACKAGES:=kmod-rtc-sunxi
@@ -246,6 +302,7 @@ endef
TARGET_DEVICES += xunlong_orangepi-zero
define Device/xunlong_orangepi-2
$(call Device/FitImageGzip)
DEVICE_VENDOR := Xunlong
DEVICE_MODEL := Orange Pi 2
DEVICE_PACKAGES:=kmod-rtc-sunxi

View File

@@ -6,6 +6,7 @@
KERNEL_LOADADDR:=0x40008000
define Device/cubietech_a10-cubieboard
$(call Device/FitImageGzip)
DEVICE_VENDOR := Cubietech
DEVICE_MODEL := Cubieboard
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-sun4i-emac kmod-rtc-sunxi
@@ -14,6 +15,7 @@ endef
TARGET_DEVICES += cubietech_a10-cubieboard
define Device/haoyu_a10-marsboard
$(call Device/FitImageGzip)
DEVICE_VENDOR := HAOYU Electronics
DEVICE_MODEL := MarsBoard A10
DEVICE_PACKAGES:=kmod-ata-core kmod-ata-sunxi kmod-sun4i-emac \
@@ -24,6 +26,7 @@ endef
TARGET_DEVICES += haoyu_a10-marsboard
define Device/linksprite_a10-pcduino
$(call Device/FitImageGzip)
DEVICE_VENDOR := LinkSprite
DEVICE_MODEL := pcDuino
DEVICE_PACKAGES:=kmod-sun4i-emac kmod-rtc-sunxi kmod-rtl8192cu
@@ -32,6 +35,7 @@ endef
TARGET_DEVICES += linksprite_a10-pcduino
define Device/olimex_a10-olinuxino-lime
$(call Device/FitImageGzip)
DEVICE_VENDOR := Olimex
DEVICE_MODEL := A10-OLinuXino-LIME
DEVICE_PACKAGES:=kmod-ata-sunxi kmod-sun4i-emac kmod-rtc-sunxi
@@ -40,6 +44,7 @@ endef
TARGET_DEVICES += olimex_a10-olinuxino-lime
define Device/olimex_a13-olimex-som
$(call Device/FitImageGzip)
DEVICE_VENDOR := Olimex
DEVICE_MODEL := A13-SOM
DEVICE_PACKAGES:=kmod-rtl8192cu
@@ -50,6 +55,7 @@ endef
TARGET_DEVICES += olimex_a13-olimex-som
define Device/olimex_a13-olinuxino
$(call Device/FitImageGzip)
DEVICE_VENDOR := Olimex
DEVICE_MODEL := A13-OLinuXino
DEVICE_PACKAGES:=kmod-rtl8192cu

View File

@@ -149,6 +149,10 @@ func (c *Conn) ReaderReplaceable() bool {
return c.disablePipe.Load() || c.deadline.Load().IsZero()
}
func (c *Conn) WriterReplaceable() bool {
return true
}
func (c *Conn) Upstream() any {
return c.ExtendedConn
}

View File

@@ -1,97 +0,0 @@
// Copy from https://github.com/WireGuard/wgctrl-go/blob/a9ab2273dd1075ea74b88c76f8757f8b4003fcbf/wgtypes/types.go#L71-L155
package generater
import (
"crypto/rand"
"encoding/base64"
"fmt"
"golang.org/x/crypto/curve25519"
)
// KeyLen is the expected key length for a WireGuard key.
const KeyLen = 32 // wgh.KeyLen
// A Key is a public, private, or pre-shared secret key. The Key constructor
// functions in this package can be used to create Keys suitable for each of
// these applications.
type Key [KeyLen]byte
// GenerateKey generates a Key suitable for use as a pre-shared secret key from
// a cryptographically safe source.
//
// The output Key should not be used as a private key; use GeneratePrivateKey
// instead.
func GenerateKey() (Key, error) {
b := make([]byte, KeyLen)
if _, err := rand.Read(b); err != nil {
return Key{}, fmt.Errorf("wgtypes: failed to read random bytes: %v", err)
}
return NewKey(b)
}
// GeneratePrivateKey generates a Key suitable for use as a private key from a
// cryptographically safe source.
func GeneratePrivateKey() (Key, error) {
key, err := GenerateKey()
if err != nil {
return Key{}, err
}
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
key[0] &= 248
key[31] &= 127
key[31] |= 64
return key, nil
}
// NewKey creates a Key from an existing byte slice. The byte slice must be
// exactly 32 bytes in length.
func NewKey(b []byte) (Key, error) {
if len(b) != KeyLen {
return Key{}, fmt.Errorf("wgtypes: incorrect key size: %d", len(b))
}
var k Key
copy(k[:], b)
return k, nil
}
// ParseKey parses a Key from a base64-encoded string, as produced by the
// Key.String method.
func ParseKey(s string) (Key, error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return Key{}, fmt.Errorf("wgtypes: failed to parse base64-encoded key: %v", err)
}
return NewKey(b)
}
// PublicKey computes a public key from the private key k.
//
// PublicKey should only be called when k is a private key.
func (k Key) PublicKey() Key {
var (
pub [KeyLen]byte
priv = [KeyLen]byte(k)
)
// ScalarBaseMult uses the correct base value per https://cr.yp.to/ecdh.html,
// so no need to specify it.
curve25519.ScalarBaseMult(&pub, &priv)
return Key(pub)
}
// String returns the base64-encoded string representation of a Key.
//
// ParseKey can be used to produce a new Key from this string.
func (k Key) String() string {
return base64.StdEncoding.EncodeToString(k[:])
}

View File

@@ -1,4 +1,4 @@
package generater
package generator
import (
"encoding/base64"
@@ -12,7 +12,7 @@ import (
func Main(args []string) {
if len(args) < 1 {
panic("Using: generate uuid/reality-keypair/wg-keypair/ech-keypair/vless-mlkem768")
panic("Using: generate uuid/reality-keypair/wg-keypair/ech-keypair/vless-mlkem768/vless-x25519")
}
switch args[0] {
case "uuid":
@@ -22,20 +22,19 @@ func Main(args []string) {
}
fmt.Println(newUUID.String())
case "reality-keypair":
privateKey, err := GeneratePrivateKey()
privateKey, err := GenX25519PrivateKey()
if err != nil {
panic(err)
}
publicKey := privateKey.PublicKey()
fmt.Println("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey[:]))
fmt.Println("PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey[:]))
fmt.Println("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey.Bytes()))
fmt.Println("PublicKey: " + base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes()))
case "wg-keypair":
privateKey, err := GeneratePrivateKey()
privateKey, err := GenX25519PrivateKey()
if err != nil {
panic(err)
}
fmt.Println("PrivateKey: " + privateKey.String())
fmt.Println("PublicKey: " + privateKey.PublicKey().String())
fmt.Println("PrivateKey: " + base64.StdEncoding.EncodeToString(privateKey.Bytes()))
fmt.Println("PublicKey: " + base64.StdEncoding.EncodeToString(privateKey.PublicKey().Bytes()))
case "ech-keypair":
if len(args) < 2 {
panic("Using: generate ech-keypair <plain_server_name>")
@@ -51,11 +50,23 @@ func Main(args []string) {
if len(args) > 1 {
seed = args[1]
}
seedBase64, clientBase64, err := encryption.GenMLKEM768(seed)
seedBase64, clientBase64, hash11Base64, err := encryption.GenMLKEM768(seed)
if err != nil {
panic(err)
}
fmt.Println("Seed: " + seedBase64)
fmt.Println("Client: " + clientBase64)
fmt.Println("Hash11: " + hash11Base64)
case "vless-x25519":
var privateKey string
if len(args) > 1 {
privateKey = args[1]
}
privateKeyBase64, passwordBase64, err := encryption.GenX25519(privateKey)
if err != nil {
panic(err)
}
fmt.Println("PrivateKey: " + privateKeyBase64)
fmt.Println("Password: " + passwordBase64)
}
}

View File

@@ -0,0 +1,27 @@
package generator
import (
"crypto/ecdh"
"crypto/rand"
)
const X25519KeySize = 32
func GenX25519PrivateKey() (*ecdh.PrivateKey, error) {
var privateKey [X25519KeySize]byte
_, err := rand.Read(privateKey[:])
if err != nil {
return nil, err
}
// Avoid generating equivalent X25519 private keys
// https://github.com/XTLS/Xray-core/pull/1747
//
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
privateKey[0] &= 248
privateKey[31] &= 127
privateKey[31] |= 64
return ecdh.X25519().NewPrivateKey(privateKey[:])
}

View File

@@ -638,8 +638,12 @@ proxies: # socks5
port: 443
uuid: uuid
network: tcp
encryption: "8min-vless-mlkem768client-bas64RawURLEncoding" # 复用八分钟后协商新的 sharedKey需小于服务端的值
# encryption: "8min-xored-mlkem768client-bas64RawURLEncoding"
# -------------------------
# vless encryption客户端配置:
# (只使用 1-RTT 模式 / 复用八分钟后协商新的 baseKey周期需小于服务端的值
# / 是只能选一个,后面是 base64RawURLEncoding使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
# -------------------------
encryption: "1rtt/8min.native/divide/random.mlkem768Client.(X25519 Password).(ML-KEM-768 Client)"
tls: false #可以不开启tls
udp: true
@@ -1359,8 +1363,12 @@ listeners:
flow: xtls-rprx-vision
# ws-path: "/" # 如果不为空则开启 websocket 传输层
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
# decryption: "10min-vless-mlkem768seed-bas64RawURLEncoding" # 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式, 后面base64字符串可由可由 mihomo generate vless-mlkem768 命令生成
# decryption: "10min-xored-mlkem768seed-bas64RawURLEncoding"
# -------------------------
# vless encryption服务端配置
# (只允许 1-RTT 模式 / 同时允许 1-RTT 模式与十分钟复用的 0-RTT 模式;原生外观 / ECH 式 XOR / 全随机数)
# / 是只能选一个,后面是 base64RawURLEncoding使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
# -------------------------
# decryption: "1rtt/10min.native/divide/random.mlkem768Seed.(X25519 PrivateKey).(ML-KEM-768 Seed)"
# 下面两项如果填写则开启 tls需要同时填写
# certificate: ./server.crt
# private-key: ./server.key

View File

@@ -31,7 +31,7 @@ require (
github.com/metacubex/sing-shadowsocks2 v0.2.6
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.7
github.com/metacubex/sing-vmess v0.2.4-0.20250819151326-51d195aac5db
github.com/metacubex/sing-vmess v0.2.4-0.20250821024956-97839f31a65d
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4

View File

@@ -131,8 +131,8 @@ github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MY
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU=
github.com/metacubex/sing-vmess v0.2.4-0.20250819151326-51d195aac5db h1:W7VKxR0r5IR+56Lblx2iyrEaykx0esdQwTQbkSrSaek=
github.com/metacubex/sing-vmess v0.2.4-0.20250819151326-51d195aac5db/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-vmess v0.2.4-0.20250821024956-97839f31a65d h1:jchYEho5+kTmok4aTMflqJyTRnqVPTOVeC1RFXxuw9A=
github.com/metacubex/sing-vmess v0.2.4-0.20250821024956-97839f31a65d/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo=

View File

@@ -21,7 +21,7 @@ import (
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/ech"
"github.com/metacubex/mihomo/component/generater"
"github.com/metacubex/mihomo/component/generator"
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
@@ -48,13 +48,12 @@ var echConfigBase64, echKeyPem, _ = ech.GenECHConfig(echPublicSni)
func init() {
rand.Read(httpData)
privateKey, err := generater.GeneratePrivateKey()
privateKey, err := generator.GenX25519PrivateKey()
if err != nil {
panic(err)
}
publicKey := privateKey.PublicKey()
realityPrivateKey = base64.RawURLEncoding.EncodeToString(privateKey[:])
realityPublickey = base64.RawURLEncoding.EncodeToString(publicKey[:])
realityPrivateKey = base64.RawURLEncoding.EncodeToString(privateKey.Bytes())
realityPublickey = base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes())
}
type TestTunnel struct {

View File

@@ -89,17 +89,29 @@ func TestInboundVless_TLS(t *testing.T) {
}
func TestInboundVless_Encryption(t *testing.T) {
seedBase64, clientBase64, err := encryption.GenMLKEM768("")
seedBase64, clientBase64, _, err := encryption.GenMLKEM768("")
if err != nil {
t.Fatal(err)
return
}
t.Run("-vless-", func(t *testing.T) {
privateKeyBase64, passwordBase64, err := encryption.GenX25519("")
if err != nil {
t.Fatal(err)
return
}
var modes = []string{
"native",
"divide",
"random",
}
for i := range modes {
mode := modes[i]
t.Run(mode, func(t *testing.T) {
inboundOptions := inbound.VlessOption{
Decryption: "10min-vless-mlkem768seed-" + seedBase64,
Decryption: "10min." + mode + ".mlkem768Seed." + privateKeyBase64 + "." + seedBase64,
}
outboundOptions := outbound.VlessOption{
Encryption: "8min-vless-mlkem768client-" + clientBase64,
Encryption: "8min." + mode + ".mlkem768Client." + passwordBase64 + "." + clientBase64,
}
testInboundVless(t, inboundOptions, outboundOptions)
t.Run("xtls-rprx-vision", func(t *testing.T) {
@@ -108,20 +120,7 @@ func TestInboundVless_Encryption(t *testing.T) {
testInboundVless(t, inboundOptions, outboundOptions)
})
})
t.Run("-xored-", func(t *testing.T) {
inboundOptions := inbound.VlessOption{
Decryption: "10min-xored-mlkem768seed-" + seedBase64,
}
outboundOptions := outbound.VlessOption{
Encryption: "8min-xored-mlkem768client-" + clientBase64,
}
testInboundVless(t, inboundOptions, outboundOptions)
t.Run("xtls-rprx-vision", func(t *testing.T) {
outboundOptions := outboundOptions
outboundOptions.Flow = "xtls-rprx-vision"
testInboundVless(t, inboundOptions, outboundOptions)
})
})
}
func TestInboundVless_Wss1(t *testing.T) {

View File

@@ -14,7 +14,7 @@ import (
"strings"
"syscall"
"github.com/metacubex/mihomo/component/generater"
"github.com/metacubex/mihomo/component/generator"
"github.com/metacubex/mihomo/component/geodata"
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/config"
@@ -73,7 +73,7 @@ func main() {
}
if len(os.Args) > 1 && os.Args[1] == "generate" {
generater.Main(os.Args[2:])
generator.Main(os.Args[2:])
return
}

View File

@@ -3,6 +3,7 @@ package encryption
import (
"bytes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"errors"
"fmt"
@@ -40,7 +41,8 @@ type ClientInstance struct {
sync.RWMutex
nfsEKey *mlkem.EncapsulationKey768
hash11 [11]byte // no more capacity
xorKey []byte
xorMode uint32
xorPKey *ecdh.PublicKey
minutes time.Duration
expire time.Time
baseKey []byte
@@ -60,22 +62,23 @@ type ClientConn struct {
input bytes.Reader // peerCache
}
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
func (i *ClientInstance) Init(nfsEKeyBytes, xorPKeyBytes []byte, xorMode, minutes uint32) (err error) {
if i.nfsEKey != nil {
err = errors.New("already initialized")
return
}
i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
if err != nil {
if i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes); err != nil {
return
}
hash32 := sha3.Sum256(nfsEKeyBytes)
copy(i.hash11[:], hash32[:])
if xor > 0 {
xorKey := sha3.Sum256(nfsEKeyBytes)
i.xorKey = xorKey[:]
if xorMode > 0 {
i.xorMode = xorMode
if i.xorPKey, err = ecdh.X25519().NewPublicKey(xorPKeyBytes); err != nil {
return
}
i.minutes = minutes
}
i.minutes = time.Duration(minutes) * time.Minute
return
}
@@ -83,8 +86,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
if i.nfsEKey == nil {
return nil, errors.New("uninitialized")
}
if i.xorKey != nil {
conn = NewXorConn(conn, i.xorKey)
if i.xorMode > 0 {
conn, _ = NewXorConn(conn, i.xorMode, i.xorPKey, nil)
}
c := &ClientConn{Conn: conn}
@@ -145,7 +148,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
}
c.baseKey = append(pfsKey, nfsKey...)
VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
VLESS, _ := NewAEAD(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
if !bytes.Equal(VLESS, []byte("VLESS")) {
return nil, errors.New("invalid server")
}
@@ -180,7 +183,7 @@ func (c *ClientConn) Write(b []byte) (int, error) {
rand.Read(c.random)
copy(data[5+32:], c.random)
EncodeHeader(data[5+32+32:], 23, len(b)+16)
c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket)
c.aead = NewAEAD(ClientCipher, c.baseKey, c.random, c.ticket)
c.nonce = make([]byte, 12)
c.aead.Seal(data[:5+32+32+5], c.nonce, b, data[5+32+32:5+32+32+5])
} else {
@@ -188,7 +191,7 @@ func (c *ClientConn) Write(b []byte) (int, error) {
EncodeHeader(data, 23, len(b)+16)
c.aead.Seal(data[:5], c.nonce, b, data[:5])
if bytes.Equal(c.nonce, MaxNonce) {
c.aead = NewAead(ClientCipher, c.baseKey, data[5:], data[:5])
c.aead = NewAEAD(ClientCipher, c.baseKey, data[5:], data[:5])
}
}
IncreaseNonce(c.nonce)
@@ -229,7 +232,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
if c.random == nil {
return 0, errors.New("empty c.random")
}
c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandomHello, c.random)
c.peerAead = NewAEAD(ClientCipher, c.baseKey, peerRandomHello, c.random)
c.peerNonce = make([]byte, 12)
}
if c.input.Len() > 0 {
@@ -252,7 +255,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
}
var peerAead cipher.AEAD
if bytes.Equal(c.peerNonce, MaxNonce) {
peerAead = NewAead(ClientCipher, c.baseKey, peerData, h)
peerAead = NewAEAD(ClientCipher, c.baseKey, peerData, h)
}
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
if peerAead != nil {

View File

@@ -73,7 +73,7 @@ func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error)
}
}
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
func NewAEAD(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
key := make([]byte, 32)
hkdf.New(sha3.New256, secret, salt, info).Read(key)
if c&1 == 1 {

View File

@@ -14,4 +14,5 @@
// https://github.com/XTLS/Xray-core/commit/d1fb48521271251a8c74bd64fcc2fc8700717a3b
// https://github.com/XTLS/Xray-core/commit/49580705f6029648399304b816a2737f991582a8
// https://github.com/XTLS/Xray-core/commit/84835bec7d0d8555d0dd30953ed26a272de814c4
// https://github.com/XTLS/Xray-core/commit/373558ed7abdbac3de41745cf30ec04c9adde604
package encryption

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"strconv"
"strings"
"time"
)
// NewClient new client from encryption string
@@ -15,7 +14,7 @@ func NewClient(encryption string) (*ClientInstance, error) {
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
return nil, nil
}
if s := strings.SplitN(encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
if s := strings.Split(encryption, "."); len(s) == 5 && s[2] == "mlkem768Client" {
var minutes uint32
if s[0] != "1rtt" {
t := strings.TrimSuffix(s[0], "min")
@@ -28,27 +27,35 @@ func NewClient(encryption string) (*ClientInstance, error) {
}
minutes = uint32(i)
}
var xor uint32
var xorMode uint32
switch s[1] {
case "vless":
case "xored":
xor = 1
case "native":
case "divide":
xorMode = 1
case "random":
xorMode = 2
default:
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
b, err := base64.RawURLEncoding.DecodeString(s[3])
xorPKeyBytes, err := base64.RawURLEncoding.DecodeString(s[3])
if err != nil {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
if len(b) == MLKEM768ClientLength {
if len(xorPKeyBytes) != X25519PasswordSize {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
nfsEKeyBytes, err := base64.RawURLEncoding.DecodeString(s[4])
if err != nil {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
if len(nfsEKeyBytes) != MLKEM768ClientLength {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
client := &ClientInstance{}
if err = client.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
if err = client.Init(nfsEKeyBytes, xorPKeyBytes, xorMode, minutes); err != nil {
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
}
return client, nil
} else {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
}
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
@@ -60,7 +67,7 @@ func NewServer(decryption string) (*ServerInstance, error) {
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
return nil, nil
}
if s := strings.SplitN(decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
if s := strings.Split(decryption, "."); len(s) == 5 && s[2] == "mlkem768Seed" {
var minutes uint32
if s[0] != "1rtt" {
t := strings.TrimSuffix(s[0], "min")
@@ -73,27 +80,35 @@ func NewServer(decryption string) (*ServerInstance, error) {
}
minutes = uint32(i)
}
var xor uint32
var xorMode uint32
switch s[1] {
case "vless":
case "xored":
xor = 1
case "native":
case "divide":
xorMode = 1
case "random":
xorMode = 2
default:
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
b, err := base64.RawURLEncoding.DecodeString(s[3])
xorSKeyBytes, err := base64.RawURLEncoding.DecodeString(s[3])
if err != nil {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
if len(b) == MLKEM768SeedLength {
if len(xorSKeyBytes) != X25519PrivateKeySize {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
nfsDKeySeed, err := base64.RawURLEncoding.DecodeString(s[4])
if err != nil {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
if len(nfsDKeySeed) != MLKEM768SeedLength {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
server := &ServerInstance{}
if err = server.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
if err = server.Init(nfsDKeySeed, xorSKeyBytes, xorMode, minutes); err != nil {
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
}
return server, nil
} else {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
}
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}

View File

@@ -1,25 +1,29 @@
package encryption
import (
"crypto/ecdh"
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/metacubex/utls/mlkem"
"golang.org/x/crypto/sha3"
)
const MLKEM768SeedLength = mlkem.SeedSize
const MLKEM768ClientLength = mlkem.EncapsulationKeySize768
const X25519PasswordSize = 32
const X25519PrivateKeySize = 32
func GenMLKEM768(seedStr string) (seedBase64, clientBase64 string, err error) {
var seed [64]byte
func GenMLKEM768(seedStr string) (seedBase64, clientBase64, hash11Base64 string, err error) {
var seed [MLKEM768SeedLength]byte
if len(seedStr) > 0 {
s, _ := base64.RawURLEncoding.DecodeString(seedStr)
if len(s) != 64 {
if len(s) != MLKEM768SeedLength {
err = fmt.Errorf("invalid length of ML-KEM-768 seed: %s", seedStr)
return
}
seed = [64]byte(s)
seed = [MLKEM768SeedLength]byte(s)
} else {
_, err = rand.Read(seed[:])
if err != nil {
@@ -28,8 +32,45 @@ func GenMLKEM768(seedStr string) (seedBase64, clientBase64 string, err error) {
}
key, _ := mlkem.NewDecapsulationKey768(seed[:])
pub := key.EncapsulationKey()
client := key.EncapsulationKey().Bytes()
hash32 := sha3.Sum256(client)
seedBase64 = base64.RawURLEncoding.EncodeToString(seed[:])
clientBase64 = base64.RawURLEncoding.EncodeToString(pub.Bytes())
clientBase64 = base64.RawURLEncoding.EncodeToString(client)
hash11Base64 = base64.RawURLEncoding.EncodeToString(hash32[:11])
return
}
func GenX25519(privateKeyStr string) (privateKeyBase64, passwordBase64 string, err error) {
var privateKey [X25519PrivateKeySize]byte
if len(privateKeyStr) > 0 {
s, _ := base64.RawURLEncoding.DecodeString(privateKeyStr)
if len(s) != X25519PrivateKeySize {
err = fmt.Errorf("invalid length of X25519 private key: %s", privateKeyStr)
return
}
privateKey = [X25519PrivateKeySize]byte(s)
} else {
_, err = rand.Read(privateKey[:])
if err != nil {
return
}
}
// Avoid generating equivalent X25519 private keys
// https://github.com/XTLS/Xray-core/pull/1747
//
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
privateKey[0] &= 248
privateKey[31] &= 127
privateKey[31] |= 64
key, err := ecdh.X25519().NewPrivateKey(privateKey[:])
if err != nil {
fmt.Println(err.Error())
return
}
privateKeyBase64 = base64.RawURLEncoding.EncodeToString(privateKey[:])
passwordBase64 = base64.RawURLEncoding.EncodeToString(key.PublicKey().Bytes())
return
}

View File

@@ -3,6 +3,7 @@ package encryption
import (
"bytes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"errors"
"fmt"
@@ -26,7 +27,8 @@ type ServerInstance struct {
sync.RWMutex
nfsDKey *mlkem.DecapsulationKey768
hash11 [11]byte // no more capacity
xorKey []byte
xorMode uint32
xorSKey *ecdh.PrivateKey
minutes time.Duration
sessions map[[32]byte]*ServerSession
closed bool
@@ -45,23 +47,24 @@ type ServerConn struct {
nonce []byte
}
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) {
func (i *ServerInstance) Init(nfsDKeySeed, xorSKeyBytes []byte, xorMode, minutes uint32) (err error) {
if i.nfsDKey != nil {
err = errors.New("already initialized")
return
}
i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
if err != nil {
if i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed); err != nil {
return
}
hash32 := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
copy(i.hash11[:], hash32[:])
if xor > 0 {
xorKey := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
i.xorKey = xorKey[:]
if xorMode > 0 {
i.xorMode = xorMode
if i.xorSKey, err = ecdh.X25519().NewPrivateKey(xorSKeyBytes); err != nil {
return
}
}
if minutes > 0 {
i.minutes = minutes
i.minutes = time.Duration(minutes) * time.Minute
i.sessions = make(map[[32]byte]*ServerSession)
go func() {
for {
@@ -95,8 +98,11 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*ServerConn, error) {
if i.nfsDKey == nil {
return nil, errors.New("uninitialized")
}
if i.xorKey != nil {
conn = NewXorConn(conn, i.xorKey)
if i.xorMode > 0 {
var err error
if conn, err = NewXorConn(conn, i.xorMode, nil, i.xorSKey); err != nil {
return nil, err
}
}
c := &ServerConn{Conn: conn}
@@ -167,7 +173,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*ServerConn, error) {
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
c.baseKey = append(pfsKey, nfsKey...)
c.ticket = append(i.hash11[:], NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...)
c.ticket = append(i.hash11[:], NewAEAD(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...)
paddingLen := randBetween(100, 1000)
@@ -221,7 +227,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
}
c.peerRandom = peerTicketHello[32:]
}
c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket)
c.peerAead = NewAEAD(c.cipher, c.baseKey, c.peerRandom, c.ticket)
c.peerNonce = make([]byte, 12)
}
if c.input.Len() > 0 {
@@ -244,7 +250,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
}
var peerAead cipher.AEAD
if bytes.Equal(c.peerNonce, MaxNonce) {
peerAead = NewAead(c.cipher, c.baseKey, peerData, h)
peerAead = NewAEAD(c.cipher, c.baseKey, peerData, h)
}
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
if peerAead != nil {
@@ -280,7 +286,7 @@ func (c *ServerConn) Write(b []byte) (int, error) {
EncodeHeader(data, 0, 32)
rand.Read(data[5 : 5+32])
EncodeHeader(data[5+32:], 23, len(b)+16)
c.aead = NewAead(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
c.aead = NewAEAD(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
c.nonce = make([]byte, 12)
c.aead.Seal(data[:5+32+5], c.nonce, b, data[5+32:5+32+5])
} else {
@@ -288,7 +294,7 @@ func (c *ServerConn) Write(b []byte) (int, error) {
EncodeHeader(data, 23, len(b)+16)
c.aead.Seal(data[:5], c.nonce, b, data[:5])
if bytes.Equal(c.nonce, MaxNonce) {
c.aead = NewAead(c.cipher, c.baseKey, data[5:], data[:5])
c.aead = NewAEAD(c.cipher, c.baseKey, data[5:], data[:5])
}
}
IncreaseNonce(c.nonce)

View File

@@ -3,13 +3,21 @@ package encryption
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"errors"
"io"
"net"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
)
type XorConn struct {
net.Conn
Divide bool
head []byte
key []byte
ctr cipher.Stream
peerCtr cipher.Stream
@@ -25,8 +33,55 @@ type XorConn struct {
in_skip int
}
func NewXorConn(conn net.Conn, key []byte) *XorConn {
return &XorConn{Conn: conn, key: key}
func NewCTR(key, iv []byte, isServer bool) cipher.Stream {
info := "CLIENT"
if isServer {
info = "SERVER" // avoids attackers sending traffic back to the client, though the encryption layer has its own protection
}
hkdf.New(sha3.New256, key, iv, []byte(info)).Read(key) // avoids using pKey directly if attackers sent the basepoint, or whaterver they like
block, _ := aes.NewCipher(key)
return cipher.NewCTR(block, iv)
}
func NewXorConn(conn net.Conn, mode uint32, pKey *ecdh.PublicKey, sKey *ecdh.PrivateKey) (*XorConn, error) {
if mode == 0 || (pKey == nil && sKey == nil) || (pKey != nil && sKey != nil) {
return nil, errors.New("invalid parameters")
}
c := &XorConn{
Conn: conn,
Divide: mode == 1,
isHeader: true,
out_header: make([]byte, 0, 5), // important
in_header: make([]byte, 0, 5), // important
}
if pKey != nil {
c.head = make([]byte, 16+32)
rand.Read(c.head)
eSKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
NewCTR(pKey.Bytes(), c.head[:16], false).XORKeyStream(c.head[16:], eSKey.PublicKey().Bytes()) // make X25519 public key distinguishable from random bytes
c.key, _ = eSKey.ECDH(pKey)
c.ctr = NewCTR(c.key, c.head[:16], false)
}
if sKey != nil {
peerHead := make([]byte, 16+32)
if _, err := io.ReadFull(c.Conn, peerHead); err != nil {
return nil, err
}
NewCTR(sKey.PublicKey().Bytes(), peerHead[:16], false).XORKeyStream(peerHead[16:], peerHead[16:]) // we don't use buggy elligator, because we have PSK :)
ePKey, err := ecdh.X25519().NewPublicKey(peerHead[16:])
if err != nil {
return nil, err
}
key, err := sKey.ECDH(ePKey)
if err != nil {
return nil, err
}
c.peerCtr = NewCTR(key, peerHead[:16], false)
c.head = make([]byte, 16)
rand.Read(c.head) // make sure the server always replies random bytes even when received replays, though it is not important
c.ctr = NewCTR(key, c.head, true) // the same key links the upload & download, though the encryption layer has its own link
}
return c, nil
//chacha20.NewUnauthenticatedCipher()
}
@@ -35,13 +90,6 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
return 0, nil
}
if !c.out_after0 {
var iv []byte
if c.ctr == nil {
block, _ := aes.NewCipher(c.key)
iv = make([]byte, 16)
rand.Read(iv)
c.ctr = cipher.NewCTR(block, iv)
}
t, l, _ := DecodeHeader(b)
if t == 23 { // single 23
l = 5
@@ -49,20 +97,24 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
l += 10
if t == 0 {
c.out_after0 = true
c.out_header = make([]byte, 0, 5) // important
if c.Divide {
l -= 5
}
}
}
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
if iv != nil {
b = append(iv, b...)
l = len(b)
if c.head != nil {
b = append(c.head, b...)
c.head = nil
}
if _, err := c.Conn.Write(b); err != nil {
return 0, err
}
if iv != nil {
b = b[16:] // for len(b)
return l, nil
}
return len(b), nil
if c.Divide {
return c.Conn.Write(b)
}
for p := b; ; { // for XTLS
if len(p) <= c.out_skip {
@@ -93,14 +145,12 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
return 0, nil
}
if !c.in_after0 || !c.isHeader {
if c.peerCtr == nil {
if c.peerCtr == nil { // for client
peerIv := make([]byte, 16)
if _, err := io.ReadFull(c.Conn, peerIv); err != nil {
return 0, err
}
block, _ := aes.NewCipher(c.key)
c.peerCtr = cipher.NewCTR(block, peerIv)
c.isHeader = true
c.peerCtr = NewCTR(c.key, peerIv, true)
}
if _, err := io.ReadFull(c.Conn, b); err != nil {
return 0, err
@@ -117,7 +167,6 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
c.isHeader = false
if t == 0 {
c.in_after0 = true
c.in_header = make([]byte, 0, 5) // important
}
}
} else {
@@ -125,6 +174,9 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
}
return len(b), nil
}
if c.Divide {
return c.Conn.Read(b)
}
n, err := c.Conn.Read(b)
for p := b[:n]; ; { // for XTLS
if len(p) <= c.in_skip {
@@ -146,3 +198,27 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
}
return n, err
}
func (c *XorConn) WriterReplaceable() bool {
if !c.Divide { // never replaceable
return false
}
if !c.out_after0 {
return false
}
return true
}
func (c *XorConn) ReaderReplaceable() bool {
if !c.Divide { // never replaceable
return false
}
if !c.in_after0 || !c.isHeader {
return false
}
return true
}
func (c *XorConn) Upstream() any {
return c.Conn
}

View File

@@ -6,12 +6,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=adguardhome
PKG_VERSION:=0.107.64
PKG_VERSION:=0.107.65
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/AdguardTeam/AdGuardHome/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=a6b61d3c102fa47072a7230382d438f3d408f74ffae3aff7d330adad90ed169c
PKG_HASH:=b770007696de88ab4de0008502002a4966a30aca88c9b030c142183a1c2a5830
PKG_BUILD_DIR:=$(BUILD_DIR)/AdGuardHome-$(PKG_VERSION)
PKG_LICENSE:=GPL-3.0-only
@@ -58,7 +58,7 @@ define Download/adguardhome-frontend
URL:=https://github.com/AdguardTeam/AdGuardHome/releases/download/v$(PKG_VERSION)/
URL_FILE:=AdGuardHome_frontend.tar.gz
FILE:=$(FRONTEND_FILE)
HASH:=f0c1e4d6e673d4d26d52947cdb220682aa554158d331fa044576d1794b82e325
HASH:=e838234d8391a23f86b32a26a5c68112a975c7ee83b79646f0597c001f6cab3f
endef
define Build/Prepare

View File

@@ -8,12 +8,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ddns-go
PKG_VERSION:=6.12.2
PKG_VERSION:=6.12.4
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=a3ea99ad74212fc3bd1380e5ad444a1c5fe6bb1bb656624a441551034a17edaa
PKG_HASH:=04f65f7f1ccc18b23dd108f915810146e8b655cd19e7d8ee488cf557222c3fee
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE

View File

@@ -10,11 +10,11 @@ include $(TOPDIR)/rules.mk
PKG_ARCH_quickstart:=$(ARCH)
PKG_NAME:=quickstart
PKG_VERSION:=0.11.2
PKG_VERSION:=0.11.3
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/prebuilt/
PKG_HASH:=b22f430f08fb12739179e4b983133afb84a37be1e702d5b4e9fb30b1e701824d
PKG_HASH:=fee17158398f7867eb20bf187fbfb2d12d46e44d2f9e882376173392a8979d14
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION)

View File

@@ -58,7 +58,8 @@ for k, e in ipairs(api.get_valid_nodes()) do
nodes_table[#nodes_table + 1] = {
id = e[".name"],
remark = e["remark"],
type = e["type"]
type = e["type"],
chain_proxy = e["chain_proxy"]
}
end
if e.protocol == "_balancing" then
@@ -696,7 +697,7 @@ o = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Onl
o:depends({ [_n("chain_proxy")] = "2" })
for k, v in pairs(nodes_table) do
if v.type == "Xray" and v.id ~= arg[1] then
if v.type == "Xray" and v.id ~= arg[1] and (not v.chain_proxy or v.chain_proxy == "") then
s.fields[_n("preproxy_node")]:value(v.id, v.remark)
s.fields[_n("to_node")]:value(v.id, v.remark)
end

View File

@@ -75,7 +75,8 @@ for k, e in ipairs(api.get_valid_nodes()) do
nodes_table[#nodes_table + 1] = {
id = e[".name"],
remark = e["remark"],
type = e["type"]
type = e["type"],
chain_proxy = e["chain_proxy"]
}
end
if e.protocol == "_iface" then
@@ -753,7 +754,7 @@ o = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Onl
o:depends({ [_n("chain_proxy")] = "2" })
for k, v in pairs(nodes_table) do
if v.type == "sing-box" and v.id ~= arg[1] then
if v.type == "sing-box" and v.id ~= arg[1] and (not v.chain_proxy or v.chain_proxy == "") then
s.fields[_n("preproxy_node")]:value(v.id, v.remark)
s.fields[_n("to_node")]:value(v.id, v.remark)
end

View File

@@ -1,3 +1,3 @@
VERSION_CODE=554
VERSION_NAME=1.12.2
VERSION_CODE=556
VERSION_NAME=1.12.3
GO_VERSION=go1.25.0

View File

@@ -7,7 +7,6 @@ import (
"errors"
"net"
mDNS "github.com/miekg/dns"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
@@ -20,6 +19,8 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
mDNS "github.com/miekg/dns"
)
func RegisterTransport(registry *dns.TransportRegistry) {

View File

@@ -2,15 +2,19 @@ package local
import (
"context"
"errors"
"os"
"sync"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/service/resolved"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/common/x/list"
"github.com/sagernet/sing/service"
"github.com/godbus/dbus/v5"
@@ -18,13 +22,20 @@ import (
)
type DBusResolvedResolver struct {
ctx context.Context
logger logger.ContextLogger
interfaceMonitor tun.DefaultInterfaceMonitor
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
systemBus *dbus.Conn
resoledObject atomic.TypedValue[dbus.BusObject]
resoledObject atomic.Pointer[ResolvedObject]
closeOnce sync.Once
}
type ResolvedObject struct {
dbus.BusObject
InterfaceIndex int32
}
func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor()
if interfaceMonitor == nil {
@@ -35,6 +46,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
return nil, err
}
return &DBusResolvedResolver{
ctx: ctx,
logger: logger,
interfaceMonitor: interfaceMonitor,
systemBus: systemBus,
@@ -43,6 +55,7 @@ func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (Reso
func (t *DBusResolvedResolver) Start() error {
t.updateStatus()
t.interfaceCallback = t.interfaceMonitor.RegisterCallback(t.updateDefaultInterface)
err := t.systemBus.BusObject().AddMatchSignal(
"org.freedesktop.DBus",
"NameOwnerChanged",
@@ -58,6 +71,9 @@ func (t *DBusResolvedResolver) Start() error {
func (t *DBusResolvedResolver) Close() error {
t.closeOnce.Do(func() {
if t.interfaceCallback != nil {
t.interfaceMonitor.UnregisterCallback(t.interfaceCallback)
}
if t.systemBus != nil {
_ = t.systemBus.Close()
}
@@ -70,22 +86,23 @@ func (t *DBusResolvedResolver) Object() any {
}
func (t *DBusResolvedResolver) Exchange(object any, ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
defaultInterface := t.interfaceMonitor.DefaultInterface()
if defaultInterface == nil {
return nil, E.New("missing default interface")
}
question := message.Question[0]
call := object.(*dbus.Object).CallWithContext(
resolvedObject := object.(*ResolvedObject)
call := resolvedObject.CallWithContext(
ctx,
"org.freedesktop.resolve1.Manager.ResolveRecord",
0,
int32(defaultInterface.Index),
resolvedObject.InterfaceIndex,
question.Name,
question.Qclass,
question.Qtype,
uint64(0),
)
if call.Err != nil {
var dbusError dbus.Error
if errors.As(call.Err, &dbusError) && dbusError.Name == "org.freedesktop.resolve1.NoNameServers" {
t.updateStatus()
}
return nil, E.Cause(call.Err, " resolve record via resolved")
}
var (
@@ -137,14 +154,76 @@ func (t *DBusResolvedResolver) loopUpdateStatus() {
}
func (t *DBusResolvedResolver) updateStatus() {
dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1")
err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err
dbusObject, err := t.checkResolved(context.Background())
oldValue := t.resoledObject.Swap(dbusObject)
if err != nil {
if t.resoledObject.Swap(nil) != nil {
var dbusErr dbus.Error
if !errors.As(err, &dbusErr) || dbusErr.Name != "org.freedesktop.DBus.Error.NameHasNoOwnerCould" {
t.logger.Debug(E.Cause(err, "systemd-resolved service unavailable"))
}
if oldValue != nil {
t.logger.Debug("systemd-resolved service is gone")
}
return
}
t.resoledObject.Store(dbusObject)
} else if oldValue == nil {
t.logger.Debug("using systemd-resolved service as resolver")
}
}
func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*ResolvedObject, error) {
dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1")
err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err
if err != nil {
return nil, err
}
defaultInterface := t.interfaceMonitor.DefaultInterface()
if defaultInterface == nil {
return nil, E.New("missing default interface")
}
call := dbusObject.(*dbus.Object).CallWithContext(
ctx,
"org.freedesktop.resolve1.Manager.GetLink",
0,
int32(defaultInterface.Index),
)
if call.Err != nil {
return nil, err
}
var linkPath dbus.ObjectPath
err = call.Store(&linkPath)
if err != nil {
return nil, err
}
linkObject := t.systemBus.Object("org.freedesktop.resolve1", linkPath)
if linkObject == nil {
return nil, E.New("missing link object for default interface")
}
dnsProp, err := linkObject.GetProperty("org.freedesktop.resolve1.Link.DNS")
if err != nil {
return nil, err
}
var linkDNS []resolved.LinkDNS
err = dnsProp.Store(&linkDNS)
if err != nil {
return nil, err
}
if len(linkDNS) == 0 {
for _, inbound := range service.FromContext[adapter.InboundManager](t.ctx).Inbounds() {
if inbound.Type() == C.TypeTun {
return nil, E.New("No appropriate name servers or networks for name found")
}
}
return &ResolvedObject{
BusObject: dbusObject,
}, nil
} else {
return &ResolvedObject{
BusObject: dbusObject,
InterfaceIndex: int32(defaultInterface.Index),
}, nil
}
}
func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) {
t.updateStatus()
}

View File

@@ -6,6 +6,14 @@ icon: material/alert-decagram
* Fixes and improvements
#### 1.12.3
* Fixes and improvements
#### 1.13.0-alpha.4
* Fixes and improvements
#### 1.12.2
* Fixes and improvements

View File

@@ -861,8 +861,8 @@ return view.extend({
so = ss.taboption('direct_list', hm.TextValue, 'direct_list.yaml', null);
so.rows = 20;
so.default = 'FQDN:\nIPCIDR:\nIPCIDR6:\n';
so.placeholder = "FQDN:\n- mask.icloud.com\n- mask-h2.icloud.com\n- mask.apple-dns.net\nIPCIDR:\n- '223.0.0.0/12'\nIPCIDR6:\n- '2400:3200::/32'\n";
so.default = 'DOMAIN:\nIPCIDR:\nIPCIDR6:\n';
so.placeholder = "DOMAIN:\n- mask.icloud.com\n- mask-h2.icloud.com\n- mask.apple-dns.net\nIPCIDR:\n- '223.0.0.0/12'\nIPCIDR6:\n- '2400:3200::/32'\n";
so.load = function(section_id) {
return L.resolveDefault(hm.readFile('resources', this.option), '');
}
@@ -879,8 +879,8 @@ return view.extend({
so = ss.taboption('proxy_list', hm.TextValue, 'proxy_list.yaml', null);
so.rows = 20;
so.default = 'FQDN:\nIPCIDR:\nIPCIDR6:\n';
so.placeholder = "FQDN:\n- www.google.com\nIPCIDR:\n- '91.105.192.0/23'\nIPCIDR6:\n- '2001:67c:4e8::/48'\n";
so.default = 'DOMAIN:\nIPCIDR:\nIPCIDR6:\n';
so.placeholder = "DOMAIN:\n- www.google.com\n- '.googlevideo.com'\n- google.com\nIPCIDR:\n- '91.105.192.0/23'\nIPCIDR6:\n- '2001:67c:4e8::/48'\n";
so.load = function(section_id) {
return L.resolveDefault(hm.readFile('resources', this.option), '');
}

View File

@@ -186,7 +186,7 @@ start_service() {
local yaml="$5"
if [ -n "$yaml" ]; then
yq '.[] |= with(select(. == null); . = []) | .FQDN[]' "$src" | \
yq '.[] |= with(select(. == null); . = []) | .DOMAIN[]' "$src" | \
sed "s|^|nftset=/|;s|$|/${family}#inet#fchomo#${set_name}|" > "$dst"
else
sed "s|^|nftset=/|;s|$|/${family}#inet#fchomo#${set_name}|" "$src" > "$dst"

View File

@@ -14,7 +14,7 @@ fi
# Initialize the default direct list
if [ ! -s "/etc/fchomo/resources/direct_list.yaml" ]; then
cat <<- EOF > "/etc/fchomo/resources/direct_list.yaml"
FQDN:
DOMAIN:
IPCIDR:
- '223.0.0.0/12'
IPCIDR6:
@@ -25,8 +25,10 @@ fi
# Initialize the default proxy list
if [ ! -s "/etc/fchomo/resources/proxy_list.yaml" ]; then
cat <<- EOF > "/etc/fchomo/resources/proxy_list.yaml"
FQDN:
DOMAIN:
- www.google.com
- '.googlevideo.com'
- google.com
IPCIDR:
- '91.105.192.0/23'
- '91.108.4.0/22'

View File

@@ -1,5 +1,8 @@
#!/bin/sh
sed -i 's|^FQDN:$|DOMAIN:|' "/etc/fchomo/resources/direct_list.yaml"
sed -i 's|^FQDN:$|DOMAIN:|' "/etc/fchomo/resources/proxy_list.yaml"
default_proxy=$(uci -q get fchomo.routing.default_proxy)
if [ -n "$default_proxy" ]; then
uci -q batch <<-EOF >"/dev/null"

View File

@@ -107,6 +107,9 @@ return baseclass.extend({
const profile = await callNikkiProfile({ 'external-controller': null, 'secret': null });
const apiListen = profile['external-controller'];
const apiSecret = profile['secret'] ?? '';
if (!apiListen) {
return Promise.reject('API has not been configured');
}
const apiPort = apiListen.substring(apiListen.lastIndexOf(':') + 1);
const url = `http://${window.location.hostname}:${apiPort}${path}`;
return request.request(url, {
@@ -114,7 +117,7 @@ return baseclass.extend({
headers: { 'Authorization': `Bearer ${apiSecret}` },
query: query,
content: body
})
});
},
openDashboard: async function () {
@@ -122,6 +125,9 @@ return baseclass.extend({
const uiName = profile['external-ui-name'];
const apiListen = profile['external-controller'];
const apiSecret = profile['secret'] ?? '';
if (!apiListen) {
return Promise.reject('API has not been configured');
}
const apiPort = apiListen.substring(apiListen.lastIndexOf(':') + 1);
const params = {
host: window.location.hostname,
@@ -137,6 +143,7 @@ return baseclass.extend({
url = `http://${window.location.hostname}:${apiPort}/ui/?${query}`;
}
setTimeout(function () { window.open(url, '_blank') }, 0);
return Promise.resolve();
},
updateDashboard: function () {

View File

@@ -58,7 +58,8 @@ for k, e in ipairs(api.get_valid_nodes()) do
nodes_table[#nodes_table + 1] = {
id = e[".name"],
remark = e["remark"],
type = e["type"]
type = e["type"],
chain_proxy = e["chain_proxy"]
}
end
if e.protocol == "_balancing" then
@@ -696,7 +697,7 @@ o = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Onl
o:depends({ [_n("chain_proxy")] = "2" })
for k, v in pairs(nodes_table) do
if v.type == "Xray" and v.id ~= arg[1] then
if v.type == "Xray" and v.id ~= arg[1] and (not v.chain_proxy or v.chain_proxy == "") then
s.fields[_n("preproxy_node")]:value(v.id, v.remark)
s.fields[_n("to_node")]:value(v.id, v.remark)
end

View File

@@ -75,7 +75,8 @@ for k, e in ipairs(api.get_valid_nodes()) do
nodes_table[#nodes_table + 1] = {
id = e[".name"],
remark = e["remark"],
type = e["type"]
type = e["type"],
chain_proxy = e["chain_proxy"]
}
end
if e.protocol == "_iface" then
@@ -753,7 +754,7 @@ o = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Onl
o:depends({ [_n("chain_proxy")] = "2" })
for k, v in pairs(nodes_table) do
if v.type == "sing-box" and v.id ~= arg[1] then
if v.type == "sing-box" and v.id ~= arg[1] and (not v.chain_proxy or v.chain_proxy == "") then
s.fields[_n("preproxy_node")]:value(v.id, v.remark)
s.fields[_n("to_node")]:value(v.id, v.remark)
end

View File

@@ -38,11 +38,13 @@ config mixin 'mixin'
option 'redir_port' '7891'
option 'tproxy_port' '7892'
option 'authentication' '1'
option 'tun_enabled' '1'
option 'tun_device' 'nikki'
option 'tun_stack' 'mixed'
option 'tun_dns_hijack' '0'
list 'tun_dns_hijacks' 'tcp://any:53'
list 'tun_dns_hijacks' 'udp://any:53'
option 'dns_enabled' '1'
option 'dns_listen' '[::]:1053'
option 'dns_ipv6' '1'
option 'dns_mode' 'fake-ip'

View File

@@ -6,12 +6,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=sing-box
PKG_VERSION:=1.12.2
PKG_VERSION:=1.12.3
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=95d902c008ed0b414ab29408dc565310fffe435a15753e02d10ca5c8e6837ce5
PKG_HASH:=3dce8ee383655908451f7f193714f0c8f90b8fd4baecb8e7e3948d263d766359
PKG_LICENSE:=GPL-3.0-or-later
PKG_LICENSE_FILES:=LICENSE

View File

@@ -21,13 +21,13 @@ define Download/geoip
HASH:=54761d8691a5756fdb08d2cd4d0a9c889dbaab786e1cf758592e09fb00377f53
endef
GEOSITE_VER:=20250820044243
GEOSITE_VER:=20250821075639
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=08eaf7b6e93ff4422eac2919673ec53f5840643ab318e891981e0f3bd51100f9
HASH:=b35fcc137b86b4db2ef23919a05c7f90267dc4783186b4e416ff10213cc62ff5
endef
GEOSITE_IRAN_VER:=202508180044

View File

@@ -99,3 +99,37 @@ jobs:
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true
# release RHEL package
- name: Package RPM (RHEL-family)
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-rhel.sh
# Build for both x86_64 and aarch64 in one go (explicit version passed; no --buildfrom)
./package-rhel.sh "${{ github.event.inputs.release_tag }}" --arch all
- name: Collect RPMs into workspace
if: github.event.inputs.release_tag != ''
run: |
mkdir -p "${{ github.workspace }}/dist/rpm"
rsync -av "$HOME/rpmbuild/RPMS/" "${{ github.workspace }}/dist/rpm/"
# Rename to requested filenames
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.x86_64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-x64.rpm" \; || true
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.aarch64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true
- name: Upload RPM artifacts
if: github.event.inputs.release_tag != ''
uses: actions/upload-artifact@v4.6.2
with:
name: v2rayN-rpm
path: |
${{ github.workspace }}/dist/rpm/**/*.rpm
- name: Upload RPMs to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/dist/rpm/**/*.rpm
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true

View File

@@ -332,6 +332,7 @@ download_xray() {
# Download Xray core and install to outdir/xray
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
mkdir -p "$outdir"
if [[ -n "${XRAY_VER:-}" ]]; then ver="${XRAY_VER}"; fi
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
@@ -353,6 +354,7 @@ download_singbox() {
# Download sing-box core and install to outdir/sing-box
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
mkdir -p "$outdir"
if [[ -n "${SING_VER:-}" ]]; then ver="${SING_VER}"; fi
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
@@ -372,6 +374,22 @@ download_singbox() {
install -Dm755 "$bin" "$outdir/sing-box"
}
# ---- NEW: download_mihomo (REQUIRED in --netcore mode) ----
download_mihomo() {
# Download mihomo into outroot/bin/mihomo/mihomo
local outroot="$1"
local url=""
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-arm64/bin/mihomo/mihomo"
else
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-64/bin/mihomo/mihomo"
fi
echo "[+] Download mihomo: $url"
mkdir -p "$outroot/bin/mihomo"
curl -fL "$url" -o "$outroot/bin/mihomo/mihomo"
chmod +x "$outroot/bin/mihomo/mihomo" || true
}
# Move geo files to a unified path: outroot/bin/xray/
unify_geo_layout() {
local outroot="$1"
@@ -451,7 +469,8 @@ download_v2rayn_bundle() {
fi
rm -f "$outroot/v2rayn.zip" 2>/dev/null || true
find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
# keep mihomo
# find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
local nested_dir
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
@@ -561,6 +580,8 @@ build_for_arch() {
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
fi
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
fi
# Tarball
@@ -583,6 +604,7 @@ Release: 1%{?dist}
Summary: v2rayN (Avalonia) GUI client for Linux (x86_64/aarch64)
License: GPL-3.0-only
URL: https://github.com/2dust/v2rayN
BugURL: https://github.com/2dust/v2rayN/issues
ExclusiveArch: aarch64 x86_64
Source0: __PKGROOT__.tar.gz
@@ -591,10 +613,11 @@ Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrende
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL
%description
v2rayN GUI client built with Avalonia.
Installs self-contained publish under /opt/v2rayN and a launcher 'v2rayn'.
Cores (if bundled): /opt/v2rayN/bin/xray, /opt/v2rayN/bin/sing_box.
Geo files for Xray are placed at /opt/v2rayN/bin/xray; launcher will symlink them into user's XDG data dir on first run.
v2rayN Linux for Red Hat Enterprise Linux
Support vless / vmess / Trojan / http / socks / Anytls / Hysteria2 / Shadowsocks / tuic / WireGuard
Support Red Hat Enterprise Linux / Fedora Linux / Rocky Linux / AlmaLinux / CentOS
For more information, Please visit our website
https://github.com/2dust/v2rayN
%prep
%setup -q -n __PKGROOT__
@@ -645,7 +668,7 @@ cat > %{buildroot}%{_datadir}/applications/v2rayn.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=v2rayN
Comment=GUI client for Xray / sing-box
Comment=v2rayN for Red Hat Enterprise Linux
Exec=v2rayn
Icon=v2rayn
Terminal=false

View File

@@ -10,6 +10,7 @@
<x:Double x:Key="IconButtonWidth">32</x:Double>
<x:Double x:Key="IconButtonHeight">32</x:Double>
<x:Double x:Key="MenuFlyoutMaxHeight">1000</x:Double>
<Thickness x:Key="Margin2">2</Thickness>
<Thickness x:Key="MarginLr4">4,0</Thickness>

View File

@@ -27,7 +27,7 @@ require (
golang.org/x/sys v0.35.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.7
google.golang.org/protobuf v1.36.8
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
h12.io/socks v1.0.3
lukechampine.com/blake3 v1.4.1

View File

@@ -145,8 +145,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -306,7 +306,8 @@ INNERTUBE_CLIENTS = {
'client': {
'clientName': 'TVHTML5',
'clientVersion': '7.20250312.16.00',
'userAgent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version',
# See: https://github.com/youtube/cobalt/blob/main/cobalt/browser/user_agent/user_agent_platform_info.cc#L506
'userAgent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/25.lts.30.1034943-gold (unlike Gecko), Unknown_TV_Unknown_0/Unknown (Unknown, Unknown)',
},
},
'INNERTUBE_CONTEXT_CLIENT_NAME': 7,