mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-02-20 23:47:21 -05:00
add shadowsocks2022 tcp client support
This commit is contained in:
parent
b583ef45b1
commit
e575a525bb
@ -25,6 +25,7 @@ type TimeoutReader interface {
|
||||
// Writer extends io.Writer with MultiBuffer.
|
||||
type Writer interface {
|
||||
// WriteMultiBuffer writes a MultiBuffer into underlying writer.
|
||||
// Caller relinquish the ownership of MultiBuffer after calling this method.
|
||||
WriteMultiBuffer(MultiBuffer) error
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
// ChunkSizeDecoder is a utility class to decode size value from bytes.
|
||||
type ChunkSizeDecoder interface {
|
||||
// SizeBytes must be stable, return the same value across all calls
|
||||
SizeBytes() int32
|
||||
Decode([]byte) (uint16, error)
|
||||
}
|
||||
|
5
go.mod
5
go.mod
@ -13,6 +13,7 @@ require (
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/jhump/protoreflect v1.15.3
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
||||
github.com/miekg/dns v1.1.57
|
||||
github.com/mustafaturan/bus v1.0.2
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
@ -36,6 +37,7 @@ require (
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gvisor.dev/gvisor v0.0.0-20231020174304-b8a429915ff1
|
||||
h12.io/socks v1.0.3
|
||||
lukechampine.com/blake3 v1.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
@ -59,7 +61,6 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.7 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
|
||||
github.com/mustafaturan/monoton v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.10.0 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
@ -81,3 +82,5 @@ require (
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
)
|
||||
|
||||
replace github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 => github.com/xiaokangwang/struc v0.0.0-20231031203518-0e381172f248
|
||||
|
6
go.sum
6
go.sum
@ -195,8 +195,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lunixbochs/struc v0.0.0-20190916212049-a5c72983bc42/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@ -330,6 +328,8 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 h1:I/ATawgO2RerCq9ACwL0wBB8xNXZdE3J+93MCEHReRs=
|
||||
github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ=
|
||||
github.com/xiaokangwang/struc v0.0.0-20231031203518-0e381172f248 h1:C1JR/QGoOd2e00yquW+C3M8R88ZZ+9oPh+ehS+mPJjQ=
|
||||
github.com/xiaokangwang/struc v0.0.0-20231031203518-0e381172f248/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/xtaci/smux v1.5.12/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
|
||||
@ -578,4 +578,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
@ -54,6 +54,8 @@ import (
|
||||
_ "github.com/v2fly/v2ray-core/v5/proxy/vlite/inbound"
|
||||
_ "github.com/v2fly/v2ray-core/v5/proxy/vlite/outbound"
|
||||
|
||||
_ "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022"
|
||||
|
||||
// Transports
|
||||
_ "github.com/v2fly/v2ray-core/v5/transport/internet/domainsocket"
|
||||
_ "github.com/v2fly/v2ray-core/v5/transport/internet/grpc"
|
||||
|
129
proxy/shadowsocks2022/client.go
Normal file
129
proxy/shadowsocks2022/client.go
Normal file
@ -0,0 +1,129 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/v2fly/v2ray-core/v5/common"
|
||||
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||
"github.com/v2fly/v2ray-core/v5/common/retry"
|
||||
"github.com/v2fly/v2ray-core/v5/common/session"
|
||||
"github.com/v2fly/v2ray-core/v5/common/signal"
|
||||
"github.com/v2fly/v2ray-core/v5/common/task"
|
||||
"github.com/v2fly/v2ray-core/v5/transport"
|
||||
"github.com/v2fly/v2ray-core/v5/transport/internet"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
config *ClientConfig
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||
outbound := session.OutboundFromContext(ctx)
|
||||
if outbound == nil || !outbound.Target.IsValid() {
|
||||
return newError("target not specified")
|
||||
}
|
||||
destination := outbound.Target
|
||||
network := destination.Network
|
||||
|
||||
var conn internet.Connection
|
||||
|
||||
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
||||
dest := net.TCPDestination(c.config.Address.AsAddress(), net.Port(c.config.Port))
|
||||
dest.Network = network
|
||||
rawConn, err := dialer.Dial(ctx, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn = rawConn
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return newError("failed to find an available destination").AtWarning().Base(err)
|
||||
}
|
||||
newError("tunneling request to ", destination, " via ", network, ":", c.config.Address).WriteToLog(session.ExportIDToError(ctx))
|
||||
defer conn.Close()
|
||||
|
||||
var keyDerivation = newBLAKE3KeyDerivation()
|
||||
var method Method
|
||||
switch c.config.Method {
|
||||
case "2022-blake3-aes-128-gcm":
|
||||
method = newAES128GCMMethod()
|
||||
case "2022-blake3-aes-256-gcm":
|
||||
method = newAES256GCMMethod()
|
||||
default:
|
||||
return newError("unknown method: ", c.config.Method)
|
||||
}
|
||||
|
||||
effectivePsk := c.config.Psk
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
timer := signal.CancelAfterInactivity(ctx, cancel, time.Minute)
|
||||
|
||||
if network == net.Network_TCP {
|
||||
request := &TCPRequest{
|
||||
keyDerivation: keyDerivation,
|
||||
method: method,
|
||||
}
|
||||
TCPRequestBuffer := buf.New()
|
||||
defer TCPRequestBuffer.Release()
|
||||
err = request.EncodeTCPRequestHeader(effectivePsk, c.config.Ipsk, destination.Address,
|
||||
int(destination.Port), nil, TCPRequestBuffer)
|
||||
if err != nil {
|
||||
return newError("failed to encode TCP request header").Base(err)
|
||||
}
|
||||
_, err = conn.Write(TCPRequestBuffer.Bytes())
|
||||
if err != nil {
|
||||
return newError("failed to write TCP request header").Base(err)
|
||||
}
|
||||
requestDone := func() error {
|
||||
encodedWriter := request.CreateClientC2SWriter(conn)
|
||||
return buf.Copy(link.Reader, encodedWriter, buf.UpdateActivity(timer))
|
||||
}
|
||||
responseDone := func() error {
|
||||
err = request.DecodeTCPResponseHeader(effectivePsk, conn)
|
||||
if err != nil {
|
||||
return newError("failed to decode TCP response header").Base(err)
|
||||
}
|
||||
if err = request.CheckC2SConnectionConstraint(); err != nil {
|
||||
return newError("C2S connection constraint violation").Base(err)
|
||||
}
|
||||
initialPayload := buf.NewWithSize(65535)
|
||||
encodedReader, err := request.CreateClientS2CReader(conn, initialPayload)
|
||||
if err != nil {
|
||||
return newError("failed to create client S2C reader").Base(err)
|
||||
}
|
||||
err = link.Writer.WriteMultiBuffer(buf.MultiBuffer{initialPayload})
|
||||
if err != nil {
|
||||
return newError("failed to write initial payload").Base(err)
|
||||
}
|
||||
return buf.Copy(encodedReader, link.Writer, buf.UpdateActivity(timer))
|
||||
}
|
||||
responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
|
||||
if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
|
||||
return newError("connection ends").Base(err)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return newError("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
||||
return &Client{
|
||||
config: config,
|
||||
ctx: ctx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
clientConfig, ok := config.(*ClientConfig)
|
||||
if !ok {
|
||||
return nil, newError("not a ClientConfig")
|
||||
}
|
||||
return NewClient(ctx, clientConfig)
|
||||
}))
|
||||
}
|
195
proxy/shadowsocks2022/config.pb.go
Normal file
195
proxy/shadowsocks2022/config.pb.go
Normal file
@ -0,0 +1,195 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
net "github.com/v2fly/v2ray-core/v5/common/net"
|
||||
_ "github.com/v2fly/v2ray-core/v5/common/protoext"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ClientConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
|
||||
Psk []byte `protobuf:"bytes,2,opt,name=psk,proto3" json:"psk,omitempty"`
|
||||
Ipsk [][]byte `protobuf:"bytes,4,rep,name=ipsk,proto3" json:"ipsk,omitempty"`
|
||||
Address *net.IPOrDomain `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Port uint32 `protobuf:"varint,6,opt,name=port,proto3" json:"port,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ClientConfig) Reset() {
|
||||
*x = ClientConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proxy_shadowsocks2022_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ClientConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ClientConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_shadowsocks2022_config_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ClientConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks2022_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetMethod() string {
|
||||
if x != nil {
|
||||
return x.Method
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetPsk() []byte {
|
||||
if x != nil {
|
||||
return x.Psk
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetIpsk() [][]byte {
|
||||
if x != nil {
|
||||
return x.Ipsk
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetAddress() *net.IPOrDomain {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientConfig) GetPort() uint32 {
|
||||
if x != nil {
|
||||
return x.Port
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_proxy_shadowsocks2022_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proxy_shadowsocks2022_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x22, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f,
|
||||
0x63, 0x6b, 0x73, 0x32, 0x30, 0x32, 0x32, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x20, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63,
|
||||
0x6b, 0x73, 0x32, 0x30, 0x32, 0x32, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e,
|
||||
0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x78,
|
||||
0x74, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x22, 0xbe, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x70,
|
||||
0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x70, 0x73, 0x6b, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x69, 0x70, 0x73, 0x6b, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x73,
|
||||
0x6b, 0x12, 0x3b, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x3a, 0x1f, 0x82, 0xb5, 0x18, 0x1b, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x12, 0x0f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x32,
|
||||
0x30, 0x32, 0x32, 0x42, 0x81, 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61,
|
||||
0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x32, 0x30, 0x32, 0x32, 0x50, 0x01, 0x5a, 0x34,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79,
|
||||
0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73,
|
||||
0x32, 0x30, 0x32, 0x32, 0xaa, 0x02, 0x20, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72,
|
||||
0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f,
|
||||
0x63, 0x6b, 0x73, 0x32, 0x30, 0x32, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_proxy_shadowsocks2022_config_proto_rawDescOnce sync.Once
|
||||
file_proxy_shadowsocks2022_config_proto_rawDescData = file_proxy_shadowsocks2022_config_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_proxy_shadowsocks2022_config_proto_rawDescGZIP() []byte {
|
||||
file_proxy_shadowsocks2022_config_proto_rawDescOnce.Do(func() {
|
||||
file_proxy_shadowsocks2022_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_shadowsocks2022_config_proto_rawDescData)
|
||||
})
|
||||
return file_proxy_shadowsocks2022_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proxy_shadowsocks2022_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_proxy_shadowsocks2022_config_proto_goTypes = []interface{}{
|
||||
(*ClientConfig)(nil), // 0: v2ray.core.proxy.shadowsocks2022.ClientConfig
|
||||
(*net.IPOrDomain)(nil), // 1: v2ray.core.common.net.IPOrDomain
|
||||
}
|
||||
var file_proxy_shadowsocks2022_config_proto_depIdxs = []int32{
|
||||
1, // 0: v2ray.core.proxy.shadowsocks2022.ClientConfig.address:type_name -> v2ray.core.common.net.IPOrDomain
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proxy_shadowsocks2022_config_proto_init() }
|
||||
func file_proxy_shadowsocks2022_config_proto_init() {
|
||||
if File_proxy_shadowsocks2022_config_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_proxy_shadowsocks2022_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ClientConfig); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proxy_shadowsocks2022_config_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_proxy_shadowsocks2022_config_proto_goTypes,
|
||||
DependencyIndexes: file_proxy_shadowsocks2022_config_proto_depIdxs,
|
||||
MessageInfos: file_proxy_shadowsocks2022_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proxy_shadowsocks2022_config_proto = out.File
|
||||
file_proxy_shadowsocks2022_config_proto_rawDesc = nil
|
||||
file_proxy_shadowsocks2022_config_proto_goTypes = nil
|
||||
file_proxy_shadowsocks2022_config_proto_depIdxs = nil
|
||||
}
|
22
proxy/shadowsocks2022/config.proto
Normal file
22
proxy/shadowsocks2022/config.proto
Normal file
@ -0,0 +1,22 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package v2ray.core.proxy.shadowsocks2022;
|
||||
option csharp_namespace = "V2Ray.Core.Proxy.Shadowsocks2022";
|
||||
option go_package = "github.com/v2fly/v2ray-core/v5/proxy/shadowsocks2022";
|
||||
option java_package = "com.v2ray.core.proxy.shadowsocks2022";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "common/net/address.proto";
|
||||
import "common/protoext/extensions.proto";
|
||||
|
||||
message ClientConfig {
|
||||
option (v2ray.core.common.protoext.message_opt).type = "outbound";
|
||||
option (v2ray.core.common.protoext.message_opt).short_name = "shadowsocks2022";
|
||||
|
||||
string method = 1;
|
||||
bytes psk = 2;
|
||||
repeated bytes ipsk = 4;
|
||||
|
||||
v2ray.core.common.net.IPOrDomain address = 5;
|
||||
uint32 port = 6;
|
||||
}
|
104
proxy/shadowsocks2022/eih_aes.go
Normal file
104
proxy/shadowsocks2022/eih_aes.go
Normal file
@ -0,0 +1,104 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"github.com/lunixbochs/struc"
|
||||
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||
"io"
|
||||
"lukechampine.com/blake3"
|
||||
)
|
||||
|
||||
func newAESEIH(size int) *aesEIH {
|
||||
return &aesEIH{length: size}
|
||||
}
|
||||
|
||||
func newAESEIHWithData(size int, eih [][aesEIHSize]byte) *aesEIH {
|
||||
return &aesEIH{length: size, eih: eih}
|
||||
}
|
||||
|
||||
const aesEIHSize = 16
|
||||
|
||||
type aesEIH struct {
|
||||
eih [][aesEIHSize]byte
|
||||
length int
|
||||
}
|
||||
|
||||
func (a *aesEIH) Pack(p []byte, opt *struc.Options) (int, error) {
|
||||
var totalCopy int
|
||||
for i := 0; i < a.length; i++ {
|
||||
n := copy(p[aesEIHSize*i:aesEIHSize*(i+1)], a.eih[i][:])
|
||||
if n != 16 {
|
||||
return 0, newError("failed to pack aesEIH")
|
||||
}
|
||||
totalCopy += n
|
||||
}
|
||||
return totalCopy, nil
|
||||
}
|
||||
|
||||
func (a *aesEIH) Unpack(r io.Reader, length int, opt *struc.Options) error {
|
||||
a.eih = make([][aesEIHSize]byte, a.length)
|
||||
for i := 0; i < a.length; i++ {
|
||||
n, err := r.Read(a.eih[i][:])
|
||||
if err != nil {
|
||||
return newError("failed to unpack aesEIH").Base(err)
|
||||
}
|
||||
if n != aesEIHSize {
|
||||
return newError("failed to unpack aesEIH")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aesEIH) Size(opt *struc.Options) int {
|
||||
return a.length * aesEIHSize
|
||||
}
|
||||
|
||||
func (a *aesEIH) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
const aesEIHPskHashSize = 16
|
||||
|
||||
type aesEIHGenerator struct {
|
||||
ipsk [][]byte
|
||||
ipskHash [][aesEIHPskHashSize]byte
|
||||
psk []byte
|
||||
pskHash [aesEIHPskHashSize]byte
|
||||
length int
|
||||
}
|
||||
|
||||
func newAESEIHGeneratorContainer(size int, effectivePsk []byte, ipsk [][]byte) *aesEIHGenerator {
|
||||
var ipskHash [][aesEIHPskHashSize]byte
|
||||
for _, v := range ipsk {
|
||||
hash := blake3.Sum512(v)
|
||||
ipskHash = append(ipskHash, [aesEIHPskHashSize]byte(hash[:16]))
|
||||
}
|
||||
pskHashFull := blake3.Sum512(effectivePsk)
|
||||
pskHash := [aesEIHPskHashSize]byte(pskHashFull[:16])
|
||||
return &aesEIHGenerator{length: size, ipsk: ipsk, ipskHash: ipskHash, psk: effectivePsk, pskHash: pskHash}
|
||||
}
|
||||
|
||||
func (a *aesEIHGenerator) GenerateEIH(derivation KeyDerivation, method Method, salt []byte) (ExtensibleIdentityHeaders, error) {
|
||||
eih := make([][16]byte, a.length)
|
||||
current := a.length - 1
|
||||
currentPskHash := a.pskHash
|
||||
for {
|
||||
identityKeyBuf := buf.New()
|
||||
identityKey := identityKeyBuf.Extend(int32(method.GetSessionSubKeyAndSaltLength()))
|
||||
err := derivation.GetIdentitySubKey(a.ipsk[current], salt, identityKey)
|
||||
if err != nil {
|
||||
return nil, newError("failed to get identity sub key").Base(err)
|
||||
}
|
||||
eih[current] = [16]byte{}
|
||||
err = method.GenerateEIH(identityKey, currentPskHash[:], eih[current][:])
|
||||
if err != nil {
|
||||
return nil, newError("failed to generate EIH").Base(err)
|
||||
}
|
||||
current--
|
||||
if current < 0 {
|
||||
break
|
||||
}
|
||||
currentPskHash = a.ipskHash[current]
|
||||
identityKeyBuf.Release()
|
||||
}
|
||||
return newAESEIHWithData(a.length, eih), nil
|
||||
}
|
255
proxy/shadowsocks2022/encoding.go
Normal file
255
proxy/shadowsocks2022/encoding.go
Normal file
@ -0,0 +1,255 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
cryptoRand "crypto/rand"
|
||||
"io"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/lunixbochs/struc"
|
||||
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||
"github.com/v2fly/v2ray-core/v5/common/crypto"
|
||||
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||
"github.com/v2fly/v2ray-core/v5/common/protocol"
|
||||
)
|
||||
|
||||
type TCPRequest struct {
|
||||
keyDerivation KeyDerivation
|
||||
method Method
|
||||
|
||||
c2sSalt RequestSalt
|
||||
c2sNonce crypto.BytesGenerator
|
||||
c2sAEAD cipher.AEAD
|
||||
|
||||
s2cSalt RequestSalt
|
||||
s2cNonce crypto.BytesGenerator
|
||||
s2cAEAD cipher.AEAD
|
||||
|
||||
s2cSaltAssert RequestSalt
|
||||
s2cInitialPayloadSize int
|
||||
}
|
||||
|
||||
func (t *TCPRequest) EncodeTCPRequestHeader(effectivePsk []byte,
|
||||
eih [][]byte, address DestinationAddress, destPort int, initialPayload []byte, Out *buf.Buffer) error {
|
||||
requestSalt := newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength())
|
||||
{
|
||||
err := requestSalt.FillAllFrom(cryptoRand.Reader)
|
||||
if err != nil {
|
||||
return newError("failed to fill salt").Base(err)
|
||||
}
|
||||
}
|
||||
t.c2sSalt = requestSalt
|
||||
var sessionKey = make([]byte, t.method.GetSessionSubKeyAndSaltLength())
|
||||
{
|
||||
err := t.keyDerivation.GetSessionSubKey(effectivePsk, requestSalt.Bytes(), sessionKey)
|
||||
if err != nil {
|
||||
return newError("failed to get session sub key").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
aead, err := t.method.GetStreamAEAD(sessionKey)
|
||||
if err != nil {
|
||||
return newError("failed to get stream AEAD").Base(err)
|
||||
}
|
||||
t.c2sAEAD = aead
|
||||
var paddingLength = TCPMinPaddingLength
|
||||
if initialPayload == nil {
|
||||
initialPayload = []byte{}
|
||||
paddingLength += rand.Intn(TCPMaxPaddingLength) // TODO INSECURE RANDOM USED
|
||||
}
|
||||
|
||||
variableLengthHeader := &TCPRequestHeader3VariableLength{
|
||||
DestinationAddress: address,
|
||||
Contents: struct {
|
||||
PaddingLength uint16 `struc:"sizeof=Padding"`
|
||||
Padding []byte
|
||||
}(struct {
|
||||
PaddingLength uint16
|
||||
Padding []byte
|
||||
}{
|
||||
PaddingLength: uint16(paddingLength),
|
||||
Padding: make([]byte, paddingLength),
|
||||
}),
|
||||
}
|
||||
variableLengthHeaderBuffer := buf.New()
|
||||
defer variableLengthHeaderBuffer.Release()
|
||||
{
|
||||
err := addrParser.WriteAddressPort(variableLengthHeaderBuffer, address, net.Port(destPort))
|
||||
if err != nil {
|
||||
return newError("failed to write address port").Base(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
err := struc.Pack(variableLengthHeaderBuffer, &variableLengthHeader.Contents)
|
||||
if err != nil {
|
||||
return newError("failed to pack variable length header").Base(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
_, err := variableLengthHeaderBuffer.Write(initialPayload)
|
||||
if err != nil {
|
||||
return newError("failed to write initial payload").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
fixedLengthHeader := &TCPRequestHeader2FixedLength{
|
||||
Type: TCPHeaderTypeClientToServerStream,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
HeaderLength: uint16(variableLengthHeaderBuffer.Len()),
|
||||
}
|
||||
|
||||
fixedLengthHeaderBuffer := buf.New()
|
||||
defer fixedLengthHeaderBuffer.Release()
|
||||
{
|
||||
err := struc.Pack(fixedLengthHeaderBuffer, fixedLengthHeader)
|
||||
if err != nil {
|
||||
return newError("failed to pack fixed length header").Base(err)
|
||||
}
|
||||
}
|
||||
eihGenerator := newAESEIHGeneratorContainer(len(eih), effectivePsk, eih)
|
||||
eihHeader, err := eihGenerator.GenerateEIH(t.keyDerivation, t.method, requestSalt.Bytes())
|
||||
if err != nil {
|
||||
return newError("failed to construct EIH").Base(err)
|
||||
}
|
||||
preSessionKeyHeader := &TCPRequestHeader1PreSessionKey{
|
||||
Salt: requestSalt,
|
||||
EIH: eihHeader,
|
||||
}
|
||||
preSessionKeyHeaderBuffer := buf.New()
|
||||
defer preSessionKeyHeaderBuffer.Release()
|
||||
{
|
||||
err := struc.Pack(preSessionKeyHeaderBuffer, preSessionKeyHeader)
|
||||
if err != nil {
|
||||
return newError("failed to pack pre session key header").Base(err)
|
||||
}
|
||||
}
|
||||
requestNonce := crypto.GenerateInitialAEADNonce()
|
||||
t.c2sNonce = requestNonce
|
||||
{
|
||||
n, err := Out.Write(preSessionKeyHeaderBuffer.BytesFrom(0))
|
||||
if err != nil {
|
||||
return newError("failed to write pre session key header").Base(err)
|
||||
}
|
||||
if int32(n) != preSessionKeyHeaderBuffer.Len() {
|
||||
return newError("failed to write pre session key header")
|
||||
}
|
||||
}
|
||||
{
|
||||
fixedLengthEncrypted := Out.Extend(fixedLengthHeaderBuffer.Len() + int32(aead.Overhead()))
|
||||
aead.Seal(fixedLengthEncrypted[:0], requestNonce(), fixedLengthHeaderBuffer.Bytes(), nil)
|
||||
}
|
||||
{
|
||||
variableLengthEncrypted := Out.Extend(variableLengthHeaderBuffer.Len() + int32(aead.Overhead()))
|
||||
aead.Seal(variableLengthEncrypted[:0], requestNonce(), variableLengthHeaderBuffer.Bytes(), nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TCPRequest) DecodeTCPResponseHeader(effectivePsk []byte, In io.Reader) error {
|
||||
var preSessionKeyHeader TCPResponseHeader1PreSessionKey
|
||||
preSessionKeyHeader.Salt = newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength())
|
||||
{
|
||||
err := struc.Unpack(In, &preSessionKeyHeader)
|
||||
if err != nil {
|
||||
return newError("failed to unpack pre session key header").Base(err)
|
||||
}
|
||||
}
|
||||
var s2cSalt = preSessionKeyHeader.Salt.Bytes()
|
||||
t.s2cSalt = preSessionKeyHeader.Salt
|
||||
var sessionKey = make([]byte, t.method.GetSessionSubKeyAndSaltLength())
|
||||
{
|
||||
err := t.keyDerivation.GetSessionSubKey(effectivePsk, s2cSalt, sessionKey)
|
||||
if err != nil {
|
||||
return newError("failed to get session sub key").Base(err)
|
||||
}
|
||||
}
|
||||
aead, err := t.method.GetStreamAEAD(sessionKey)
|
||||
if err != nil {
|
||||
return newError("failed to get stream AEAD").Base(err)
|
||||
}
|
||||
t.s2cAEAD = aead
|
||||
|
||||
var fixedLengthHeaderEncryptedBuffer = buf.New()
|
||||
defer fixedLengthHeaderEncryptedBuffer.Release()
|
||||
{
|
||||
_, err := fixedLengthHeaderEncryptedBuffer.ReadFullFrom(In, 11+int32(t.method.GetSessionSubKeyAndSaltLength())+int32(aead.Overhead()))
|
||||
if err != nil {
|
||||
return newError("failed to read fixed length header encrypted").Base(err)
|
||||
}
|
||||
}
|
||||
s2cNonce := crypto.GenerateInitialAEADNonce()
|
||||
t.s2cNonce = s2cNonce
|
||||
var fixedLengthHeaderDecryptedBuffer = buf.New()
|
||||
defer fixedLengthHeaderDecryptedBuffer.Release()
|
||||
{
|
||||
decryptionBuffer := fixedLengthHeaderDecryptedBuffer.Extend(11 + int32(t.method.GetSessionSubKeyAndSaltLength()))
|
||||
_, err = aead.Open(decryptionBuffer[:0], s2cNonce(), fixedLengthHeaderEncryptedBuffer.Bytes(), nil)
|
||||
if err != nil {
|
||||
return newError("failed to decrypt fixed length header").Base(err)
|
||||
}
|
||||
}
|
||||
var fixedLengthHeader TCPResponseHeader2FixedLength
|
||||
fixedLengthHeader.RequestSalt = newRequestSaltWithLength(t.method.GetSessionSubKeyAndSaltLength())
|
||||
{
|
||||
err := struc.Unpack(bytes.NewReader(fixedLengthHeaderDecryptedBuffer.Bytes()), &fixedLengthHeader)
|
||||
if err != nil {
|
||||
return newError("failed to unpack fixed length header").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
if fixedLengthHeader.Type != TCPHeaderTypeServerToClientStream {
|
||||
return newError("unexpected TCP header type")
|
||||
}
|
||||
timeDifference := int64(fixedLengthHeader.Timestamp) - time.Now().Unix()
|
||||
if timeDifference < -60 || timeDifference > 60 {
|
||||
return newError("timestamp is too far away")
|
||||
}
|
||||
|
||||
t.s2cSaltAssert = fixedLengthHeader.RequestSalt
|
||||
t.s2cInitialPayloadSize = int(fixedLengthHeader.InitialPayloadLength)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TCPRequest) CheckC2SConnectionConstraint() error {
|
||||
if bytes.Compare(t.c2sSalt.Bytes(), t.s2cSaltAssert.Bytes()) != 0 {
|
||||
return newError("c2s salt not equal to s2c salt assert")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TCPRequest) CreateClientS2CReader(In io.Reader, initialPayload *buf.Buffer) (buf.Reader, error) {
|
||||
AEADAuthenticator := &crypto.AEADAuthenticator{
|
||||
AEAD: t.s2cAEAD,
|
||||
NonceGenerator: t.s2cNonce,
|
||||
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
||||
}
|
||||
initialPayloadEncrypted := buf.NewWithSize(65535)
|
||||
defer initialPayloadEncrypted.Release()
|
||||
initialPayloadEncryptedBytes := initialPayloadEncrypted.Extend(int32(t.s2cAEAD.Overhead()) + int32(t.s2cInitialPayloadSize))
|
||||
_, err := io.ReadFull(In, initialPayloadEncryptedBytes)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read initial payload").Base(err)
|
||||
}
|
||||
initialPayloadBytes := initialPayload.Extend(int32(t.s2cInitialPayloadSize))
|
||||
_, err = t.s2cAEAD.Open(initialPayloadBytes[:0], t.s2cNonce(), initialPayloadEncryptedBytes, nil)
|
||||
if err != nil {
|
||||
return nil, newError("failed to decrypt initial payload").Base(err)
|
||||
}
|
||||
return crypto.NewAuthenticationReader(AEADAuthenticator, &crypto.AEADChunkSizeParser{
|
||||
Auth: AEADAuthenticator,
|
||||
}, In, protocol.TransferTypeStream, nil), nil
|
||||
}
|
||||
|
||||
func (t *TCPRequest) CreateClientC2SWriter(writer io.Writer) buf.Writer {
|
||||
AEADAuthenticator := &crypto.AEADAuthenticator{
|
||||
AEAD: t.c2sAEAD,
|
||||
NonceGenerator: t.c2sNonce,
|
||||
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
|
||||
}
|
||||
sizeParser := &crypto.AEADChunkSizeParser{
|
||||
Auth: AEADAuthenticator,
|
||||
}
|
||||
return crypto.NewAuthenticationWriter(AEADAuthenticator, sizeParser, writer, protocol.TransferTypeStream, nil)
|
||||
}
|
9
proxy/shadowsocks2022/errors.generated.go
Normal file
9
proxy/shadowsocks2022/errors.generated.go
Normal file
@ -0,0 +1,9 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import "github.com/v2fly/v2ray-core/v5/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
31
proxy/shadowsocks2022/kdf_blake3.go
Normal file
31
proxy/shadowsocks2022/kdf_blake3.go
Normal file
@ -0,0 +1,31 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||
"lukechampine.com/blake3"
|
||||
)
|
||||
|
||||
func newBLAKE3KeyDerivation() *BLAKE3KeyDerivation {
|
||||
return &BLAKE3KeyDerivation{}
|
||||
}
|
||||
|
||||
type BLAKE3KeyDerivation struct {
|
||||
}
|
||||
|
||||
func (B BLAKE3KeyDerivation) GetSessionSubKey(effectivePsk, Salt []byte, OutKey []byte) error {
|
||||
keyingMaterialBuffer := buf.New()
|
||||
keyingMaterialBuffer.Write(effectivePsk)
|
||||
keyingMaterialBuffer.Write(Salt)
|
||||
blake3.DeriveKey(OutKey, "shadowsocks 2022 session subkey", keyingMaterialBuffer.Bytes())
|
||||
keyingMaterialBuffer.Release()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (B BLAKE3KeyDerivation) GetIdentitySubKey(effectivePsk, Salt []byte, OutKey []byte) error {
|
||||
keyingMaterialBuffer := buf.New()
|
||||
keyingMaterialBuffer.Write(effectivePsk)
|
||||
keyingMaterialBuffer.Write(Salt)
|
||||
blake3.DeriveKey(OutKey, "shadowsocks 2022 identity subkey", keyingMaterialBuffer.Bytes())
|
||||
keyingMaterialBuffer.Release()
|
||||
return nil
|
||||
}
|
38
proxy/shadowsocks2022/method_aes128gcm.go
Normal file
38
proxy/shadowsocks2022/method_aes128gcm.go
Normal file
@ -0,0 +1,38 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
func newAES128GCMMethod() *AES128GCMMethod {
|
||||
return &AES128GCMMethod{}
|
||||
}
|
||||
|
||||
type AES128GCMMethod struct {
|
||||
}
|
||||
|
||||
func (A AES128GCMMethod) GetSessionSubKeyAndSaltLength() int {
|
||||
return 16
|
||||
}
|
||||
|
||||
func (A AES128GCMMethod) GetStreamAEAD(SessionSubKey []byte) (cipher.AEAD, error) {
|
||||
aesCipher, err := aes.NewCipher(SessionSubKey)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create AES cipher").Base(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aesCipher)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create AES-GCM AEAD").Base(err)
|
||||
}
|
||||
return aead, nil
|
||||
}
|
||||
|
||||
func (A AES128GCMMethod) GenerateEIH(CurrentIdentitySubKey []byte, nextPskHash []byte, out []byte) error {
|
||||
aesCipher, err := aes.NewCipher(CurrentIdentitySubKey)
|
||||
if err != nil {
|
||||
return newError("failed to create AES cipher").Base(err)
|
||||
}
|
||||
aesCipher.Encrypt(out, nextPskHash)
|
||||
return nil
|
||||
}
|
38
proxy/shadowsocks2022/method_aes256gcm.go
Normal file
38
proxy/shadowsocks2022/method_aes256gcm.go
Normal file
@ -0,0 +1,38 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
func newAES256GCMMethod() *AES256GCMMethod {
|
||||
return &AES256GCMMethod{}
|
||||
}
|
||||
|
||||
type AES256GCMMethod struct {
|
||||
}
|
||||
|
||||
func (A AES256GCMMethod) GetSessionSubKeyAndSaltLength() int {
|
||||
return 32
|
||||
}
|
||||
|
||||
func (A AES256GCMMethod) GetStreamAEAD(SessionSubKey []byte) (cipher.AEAD, error) {
|
||||
aesCipher, err := aes.NewCipher(SessionSubKey)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create AES cipher").Base(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aesCipher)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create AES-GCM AEAD").Base(err)
|
||||
}
|
||||
return aead, nil
|
||||
}
|
||||
|
||||
func (A AES256GCMMethod) GenerateEIH(CurrentIdentitySubKey []byte, nextPskHash []byte, out []byte) error {
|
||||
aesCipher, err := aes.NewCipher(CurrentIdentitySubKey)
|
||||
if err != nil {
|
||||
return newError("failed to create AES cipher").Base(err)
|
||||
}
|
||||
aesCipher.Encrypt(out, nextPskHash)
|
||||
return nil
|
||||
}
|
58
proxy/shadowsocks2022/requestsalt.go
Normal file
58
proxy/shadowsocks2022/requestsalt.go
Normal file
@ -0,0 +1,58 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/lunixbochs/struc"
|
||||
"io"
|
||||
)
|
||||
|
||||
func newRequestSaltWithLength(length int) RequestSalt {
|
||||
return &requestSaltWithLength{length: length}
|
||||
}
|
||||
|
||||
type requestSaltWithLength struct {
|
||||
length int
|
||||
content []byte
|
||||
}
|
||||
|
||||
func (r *requestSaltWithLength) isRequestSalt() {}
|
||||
func (r *requestSaltWithLength) Pack(p []byte, opt *struc.Options) (int, error) {
|
||||
n := copy(p, r.content)
|
||||
if n != r.length {
|
||||
return 0, newError("failed to pack request salt with length")
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (r *requestSaltWithLength) Unpack(reader io.Reader, length int, opt *struc.Options) error {
|
||||
r.content = make([]byte, r.length)
|
||||
n, err := io.ReadFull(reader, r.content)
|
||||
if err != nil {
|
||||
return newError("failed to unpack request salt with length").Base(err)
|
||||
}
|
||||
if n != r.length {
|
||||
return newError("failed to unpack request salt with length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *requestSaltWithLength) Size(opt *struc.Options) int {
|
||||
return r.length
|
||||
}
|
||||
|
||||
func (r *requestSaltWithLength) String() string {
|
||||
return hex.Dump(r.content)
|
||||
}
|
||||
|
||||
func (r *requestSaltWithLength) Bytes() []byte {
|
||||
return r.content
|
||||
}
|
||||
|
||||
func (r *requestSaltWithLength) FillAllFrom(reader io.Reader) error {
|
||||
r.content = make([]byte, r.length)
|
||||
_, err := io.ReadFull(reader, r.content)
|
||||
if err != nil {
|
||||
return newError("failed to fill salt from reader").Base(err)
|
||||
}
|
||||
return nil
|
||||
}
|
88
proxy/shadowsocks2022/ss2022.go
Normal file
88
proxy/shadowsocks2022/ss2022.go
Normal file
@ -0,0 +1,88 @@
|
||||
package shadowsocks2022
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"github.com/lunixbochs/struc"
|
||||
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||
"github.com/v2fly/v2ray-core/v5/common/protocol"
|
||||
"io"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
|
||||
|
||||
type KeyDerivation interface {
|
||||
GetSessionSubKey(effectivePsk, Salt []byte, OutKey []byte) error
|
||||
GetIdentitySubKey(effectivePsk, Salt []byte, OutKey []byte) error
|
||||
}
|
||||
|
||||
type Method interface {
|
||||
GetSessionSubKeyAndSaltLength() int
|
||||
GetStreamAEAD(SessionSubKey []byte) (cipher.AEAD, error)
|
||||
GenerateEIH(CurrentIdentitySubKey []byte, nextPskHash []byte, out []byte) error
|
||||
}
|
||||
|
||||
type ExtensibleIdentityHeaders interface {
|
||||
struc.Custom
|
||||
}
|
||||
|
||||
type DestinationAddress interface {
|
||||
net.Address
|
||||
}
|
||||
|
||||
type RequestSalt interface {
|
||||
struc.Custom
|
||||
isRequestSalt()
|
||||
Bytes() []byte
|
||||
FillAllFrom(reader io.Reader) error
|
||||
}
|
||||
|
||||
type TCPRequestHeader1PreSessionKey struct {
|
||||
Salt RequestSalt
|
||||
EIH ExtensibleIdentityHeaders
|
||||
}
|
||||
|
||||
type TCPRequestHeader2FixedLength struct {
|
||||
Type byte
|
||||
Timestamp uint64
|
||||
HeaderLength uint16
|
||||
}
|
||||
|
||||
type TCPRequestHeader3VariableLength struct {
|
||||
DestinationAddress DestinationAddress
|
||||
Contents struct {
|
||||
PaddingLength uint16 `struc:"sizeof=Padding"`
|
||||
Padding []byte
|
||||
}
|
||||
}
|
||||
|
||||
type TCPRequestHeader struct {
|
||||
PreSessionKeyHeader TCPRequestHeader1PreSessionKey
|
||||
FixedLengthHeader TCPRequestHeader2FixedLength
|
||||
Header TCPRequestHeader3VariableLength
|
||||
}
|
||||
|
||||
type TCPResponseHeader1PreSessionKey struct {
|
||||
Salt RequestSalt
|
||||
}
|
||||
|
||||
type TCPResponseHeader2FixedLength struct {
|
||||
Type byte
|
||||
Timestamp uint64
|
||||
RequestSalt RequestSalt
|
||||
InitialPayloadLength uint16
|
||||
}
|
||||
type TCPResponseHeader struct {
|
||||
PreSessionKeyHeader TCPResponseHeader1PreSessionKey
|
||||
Header TCPResponseHeader2FixedLength
|
||||
}
|
||||
|
||||
const TCPHeaderTypeClientToServerStream = byte(0x00)
|
||||
const TCPHeaderTypeServerToClientStream = byte(0x01)
|
||||
const TCPMinPaddingLength = 0
|
||||
const TCPMaxPaddingLength = 900
|
||||
|
||||
var addrParser = protocol.NewAddressParser(
|
||||
protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4),
|
||||
protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6),
|
||||
protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain),
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user