Add shadowsocks multi service

This commit is contained in:
世界 2022-04-30 20:48:31 +08:00
parent 5487ec681e
commit 41e416c4f4
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
7 changed files with 376 additions and 65 deletions

View file

@ -1,6 +1,8 @@
package shadowsocks
import (
"context"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/protocol/socks"
@ -16,3 +18,14 @@ type Handler interface {
socks.UDPConnectionHandler
E.Handler
}
type MultiUserService[U comparable] interface {
Service
AddUser(user U, key []byte)
RemoveUser(user U)
}
type UserContext[U comparable] struct {
context.Context
User U
}

View file

@ -62,7 +62,7 @@ var List = []string{
"2022-blake3-chacha20-poly1305",
}
func New(method string, pskList [][]byte, secureRNG io.Reader) (shadowsocks.Method, error) {
func New(method string, pskList [][KeySaltSize]byte, secureRNG io.Reader) (shadowsocks.Method, error) {
m := &Method{
name: method,
psk: pskList[len(pskList)-1],
@ -71,19 +71,13 @@ func New(method string, pskList [][]byte, secureRNG io.Reader) (shadowsocks.Meth
replayFilter: replay.NewCuckoo(60),
}
for _, psk := range pskList {
if len(psk) != KeySaltSize {
return nil, shadowaead.ErrBadKey
}
}
if len(pskList) > 1 {
pskHash := make([]byte, len(pskList)-1*aes.BlockSize)
for i, psk := range pskList {
if i == 0 {
continue
}
hash := blake3.Sum512(psk)
hash := blake3.Sum512(psk[:])
copy(pskHash[aes.BlockSize*(i-1):aes.BlockSize*i], hash[:aes.BlockSize])
}
m.pskHash = pskHash
@ -94,23 +88,23 @@ func New(method string, pskList [][]byte, secureRNG io.Reader) (shadowsocks.Meth
m.keyLength = 16
m.constructor = newAESGCM
m.blockConstructor = newAES
m.udpBlockCipher = newAES(m.psk)
m.udpBlockCipher = newAES(pskList[0][:])
case "2022-blake3-aes-256-gcm":
m.keyLength = 32
m.constructor = newAESGCM
m.blockConstructor = newAES
m.udpBlockCipher = newAES(m.psk)
m.udpBlockCipher = newAES(pskList[0][:])
case "2022-blake3-chacha20-poly1305":
m.keyLength = 32
m.constructor = newChacha20Poly1305
m.udpCipher = newXChacha20Poly1305(m.psk)
m.udpCipher = newXChacha20Poly1305(m.psk[:])
}
return m, nil
}
func Blake3DeriveKey(psk, salt []byte, keyLength int) []byte {
func Blake3DeriveKey(psk []byte, salt []byte, keyLength int) []byte {
sessionKey := buf.Make(len(psk) + len(salt))
copy(sessionKey, psk)
copy(sessionKey, psk[:])
copy(sessionKey[len(psk):], salt)
outKey := buf.Make(keyLength)
blake3.DeriveKey(outKey, "shadowsocks 2022 session subkey", sessionKey)
@ -150,8 +144,8 @@ type Method struct {
blockConstructor func(key []byte) cipher.Block
udpCipher cipher.AEAD
udpBlockCipher cipher.Block
psk []byte
pskList [][]byte
psk [KeySaltSize]byte
pskList [][KeySaltSize]byte
pskHash []byte
secureRNG io.Reader
replayFilter replay.Filter
@ -208,7 +202,7 @@ func (m *Method) writeExtendedIdentityHeaders(request *buf.Buffer, salt []byte)
}
for i, psk := range m.pskList {
keyMaterial := make([]byte, 2*KeySaltSize)
copy(keyMaterial, psk)
copy(keyMaterial, psk[:])
copy(keyMaterial[KeySaltSize:], salt)
_identitySubkey := buf.Make(m.keyLength)
identitySubkey := common.Dup(_identitySubkey)
@ -225,7 +219,7 @@ func (c *clientConn) writeRequest(payload []byte) error {
salt := make([]byte, KeySaltSize)
common.Must1(io.ReadFull(c.method.secureRNG, salt))
key := Blake3DeriveKey(c.method.psk, salt, c.method.keyLength)
key := Blake3DeriveKey(c.method.psk[:], salt, c.method.keyLength)
writer := shadowaead.NewWriter(
c.Conn,
c.method.constructor(common.Dup(key)),
@ -300,7 +294,7 @@ func (c *clientConn) readResponse() error {
return E.New("salt not unique")
}
key := Blake3DeriveKey(c.method.psk, salt, c.method.keyLength)
key := Blake3DeriveKey(c.method.psk[:], salt, c.method.keyLength)
reader := shadowaead.NewReader(
c.Conn,
c.method.constructor(common.Dup(key)),
@ -418,7 +412,7 @@ func (c *clientPacketConn) WritePacket(buffer *buf.Buffer, destination *M.AddrPo
for textI := 0; textI < aes.BlockSize; textI++ {
identityHeader[textI] = pskHash[textI] ^ header.Byte(textI)
}
c.method.blockConstructor(psk).Encrypt(identityHeader, identityHeader)
c.method.blockConstructor(psk[:]).Encrypt(identityHeader, identityHeader)
if i == pskLen-2 {
break
}
@ -485,7 +479,7 @@ func (c *clientPacketConn) ReadPacket(buffer *buf.Buffer) (*M.AddrPort, error) {
} else if sessionId == c.session.lastRemoteSessionId {
remoteCipher = c.session.lastRemoteCipher
} else {
key := Blake3DeriveKey(c.method.psk, packetHeader[:8], c.method.keyLength)
key := Blake3DeriveKey(c.method.psk[:], packetHeader[:8], c.method.keyLength)
remoteCipher = c.method.constructor(common.Dup(key))
}
_, err = remoteCipher.Open(buffer.Index(0), packetHeader[4:16], buffer.Bytes(), nil)
@ -583,7 +577,7 @@ func (m *Method) newUDPSession() *udpSession {
if m.udpCipher == nil {
sessionId := make([]byte, 8)
binary.BigEndian.PutUint64(sessionId, session.sessionId)
key := Blake3DeriveKey(m.psk, sessionId, m.keyLength)
key := Blake3DeriveKey(m.psk[:], sessionId, m.keyLength)
session.cipher = m.constructor(common.Dup(key))
}
return session

View file

@ -34,14 +34,14 @@ type Service struct {
blockConstructor func(key []byte) cipher.Block
udpCipher cipher.AEAD
udpBlockCipher cipher.Block
psk []byte
psk [KeySaltSize]byte
replayFilter replay.Filter
handler shadowsocks.Handler
udpNat udpnat.Service[uint64]
sessions cache.LruCache[uint64, *serverUDPSession]
}
func NewService(method string, psk []byte, secureRNG io.Reader, udpTimeout int64, handler shadowsocks.Handler) (shadowsocks.Service, error) {
func NewService(method string, psk [KeySaltSize]byte, secureRNG io.Reader, udpTimeout int64, handler shadowsocks.Handler) (shadowsocks.Service, error) {
s := &Service{
name: method,
psk: psk,
@ -51,25 +51,21 @@ func NewService(method string, psk []byte, secureRNG io.Reader, udpTimeout int64
sessions: cache.NewLRU[uint64, *serverUDPSession](udpTimeout, true),
}
if len(psk) != KeySaltSize {
return nil, shadowaead.ErrBadKey
}
switch method {
case "2022-blake3-aes-128-gcm":
s.keyLength = 16
s.constructor = newAESGCM
s.blockConstructor = newAES
s.udpBlockCipher = newAES(s.psk)
s.udpBlockCipher = newAES(s.psk[:])
case "2022-blake3-aes-256-gcm":
s.keyLength = 32
s.constructor = newAESGCM
s.blockConstructor = newAES
s.udpBlockCipher = newAES(s.psk)
s.udpBlockCipher = newAES(s.psk[:])
case "2022-blake3-chacha20-poly1305":
s.keyLength = 32
s.constructor = newChacha20Poly1305
s.udpCipher = newXChacha20Poly1305(s.psk)
s.udpCipher = newXChacha20Poly1305(s.psk[:])
}
s.udpNat = udpnat.New[uint64](udpTimeout, s)
@ -87,7 +83,7 @@ func (s *Service) NewConnection(ctx context.Context, conn net.Conn, metadata M.M
return E.New("salt not unique")
}
requestKey := Blake3DeriveKey(s.psk, requestSalt, s.keyLength)
requestKey := Blake3DeriveKey(s.psk[:], requestSalt, s.keyLength)
reader := shadowaead.NewReader(
conn,
s.constructor(common.Dup(requestKey)),
@ -135,6 +131,7 @@ func (s *Service) NewConnection(ctx context.Context, conn net.Conn, metadata M.M
return s.handler.NewConnection(ctx, &serverConn{
Service: s,
Conn: conn,
uPSK: s.psk,
reader: reader,
requestSalt: requestSalt,
}, metadata)
@ -143,6 +140,7 @@ func (s *Service) NewConnection(ctx context.Context, conn net.Conn, metadata M.M
type serverConn struct {
*Service
net.Conn
uPSK [KeySaltSize]byte
access sync.Mutex
reader *shadowaead.Reader
writer *shadowaead.Writer
@ -150,10 +148,10 @@ type serverConn struct {
}
func (c *serverConn) writeResponse(payload []byte) (n int, err error) {
_salt := make([]byte, KeySaltSize)
salt := common.Dup(_salt)
var _salt [KeySaltSize]byte
salt := common.Dup(_salt[:])
common.Must1(io.ReadFull(c.secureRNG, salt))
key := Blake3DeriveKey(c.psk, salt, c.keyLength)
key := Blake3DeriveKey(c.uPSK[:], salt, c.keyLength)
writer := shadowaead.NewWriter(
c.Conn,
c.constructor(common.Dup(key)),
@ -165,7 +163,7 @@ func (c *serverConn) writeResponse(payload []byte) (n int, err error) {
common.Must(rw.WriteByte(bufferedWriter, HeaderTypeServer))
common.Must(binary.Write(bufferedWriter, binary.BigEndian, uint64(time.Now().Unix())))
common.Must1(bufferedWriter.Write(c.requestSalt))
common.Must1(bufferedWriter.Write(c.requestSalt[:]))
c.requestSalt = nil
if len(payload) > 0 {
@ -236,7 +234,7 @@ func (s *Service) NewPacket(conn socks.PacketConn, buffer *buf.Buffer, metadata
if !loaded {
session.remoteSessionId = sessionId
if packetHeader != nil {
key := Blake3DeriveKey(s.psk, packetHeader[:8], s.keyLength)
key := Blake3DeriveKey(s.psk[:], packetHeader[:8], s.keyLength)
session.remoteCipher = s.constructor(common.Dup(key))
}
}
@ -376,7 +374,7 @@ func (m *Service) newUDPSession() *serverUDPSession {
if m.udpCipher == nil {
sessionId := make([]byte, 8)
binary.BigEndian.PutUint64(sessionId, session.sessionId)
key := Blake3DeriveKey(m.psk, sessionId, m.keyLength)
key := Blake3DeriveKey(m.psk[:], sessionId, m.keyLength)
session.cipher = m.constructor(common.Dup(key))
}
return session

View file

@ -0,0 +1,286 @@
package shadowaead_2022
import (
"context"
"crypto/aes"
"encoding/binary"
"io"
"math"
"net"
"time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/protocol/shadowsocks"
"github.com/sagernet/sing/protocol/shadowsocks/shadowaead"
"github.com/sagernet/sing/protocol/socks"
"lukechampine.com/blake3"
)
type MultiService[U comparable] struct {
*Service
uPSK map[U][KeySaltSize]byte
uPSKHash map[U][aes.BlockSize]byte
uPSKHashR map[[aes.BlockSize]byte]U
}
func (s *MultiService[U]) AddUser(user U, key []byte) {
var uPSKHash [aes.BlockSize]byte
hash512 := blake3.Sum512(key)
copy(uPSKHash[:], hash512[:])
if oldHash, loaded := s.uPSKHash[user]; loaded {
delete(s.uPSKHashR, oldHash)
}
s.uPSKHash[user] = uPSKHash
s.uPSKHashR[uPSKHash] = user
var uPSK [KeySaltSize]byte
copy(uPSK[:], key)
s.uPSK[user] = uPSK
}
func (s *MultiService[U]) RemoveUser(user U) {
if hash, loaded := s.uPSKHash[user]; loaded {
delete(s.uPSKHashR, hash)
}
delete(s.uPSK, user)
delete(s.uPSKHash, user)
}
func NewMultiService[U comparable](method string, iPSK [KeySaltSize]byte, secureRNG io.Reader, udpTimeout int64, handler shadowsocks.Handler) (shadowsocks.MultiUserService[U], error) {
switch method {
case "2022-blake3-aes-128-gcm":
case "2022-blake3-aes-256-gcm":
default:
return nil, E.New("unsupported method ", method)
}
ss, err := NewService(method, iPSK, secureRNG, udpTimeout, handler)
if err != nil {
return nil, err
}
s := &MultiService[U]{
Service: ss.(*Service),
}
return s, nil
}
func (s *MultiService[U]) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
requestSalt := make([]byte, KeySaltSize)
_, err := io.ReadFull(conn, requestSalt)
if err != nil {
return E.Cause(err, "read request salt")
}
if !s.replayFilter.Check(requestSalt) {
return E.New("salt not unique")
}
var _eiHeader [aes.BlockSize]byte
eiHeader := common.Dup(_eiHeader[:])
_, err = io.ReadFull(conn, eiHeader)
if err != nil {
return E.Cause(err, "read extended identity header")
}
keyMaterial := make([]byte, 2*KeySaltSize)
copy(keyMaterial, s.psk[:])
copy(keyMaterial[KeySaltSize:], requestSalt)
_identitySubkey := buf.Make(s.keyLength)
identitySubkey := common.Dup(_identitySubkey)
blake3.DeriveKey(identitySubkey, "shadowsocks 2022 identity subkey", keyMaterial)
s.blockConstructor(identitySubkey).Decrypt(eiHeader, eiHeader)
var user U
var uPSK [KeySaltSize]byte
if u, loaded := s.uPSKHashR[_eiHeader]; loaded {
user = u
uPSK = s.uPSK[u]
} else {
return E.New("invalid request")
}
requestKey := Blake3DeriveKey(uPSK[:], requestSalt, s.keyLength)
reader := shadowaead.NewReader(
conn,
s.constructor(common.Dup(requestKey)),
MaxPacketSize,
)
headerType, err := rw.ReadByte(reader)
if err != nil {
return E.Cause(err, "read header")
}
if headerType != HeaderTypeClient {
return ErrBadHeaderType
}
var epoch uint64
err = binary.Read(reader, binary.BigEndian, &epoch)
if err != nil {
return E.Cause(err, "read timestamp")
}
if math.Abs(float64(time.Now().Unix()-int64(epoch))) > 30 {
return ErrBadTimestamp
}
destination, err := socks.AddressSerializer.ReadAddrPort(reader)
if err != nil {
return E.Cause(err, "read destination")
}
var paddingLen uint16
err = binary.Read(reader, binary.BigEndian, &paddingLen)
if err != nil {
return E.Cause(err, "read padding length")
}
if paddingLen > 0 {
err = reader.Discard(int(paddingLen))
if err != nil {
return E.Cause(err, "discard padding")
}
}
var userCtx shadowsocks.UserContext[U]
userCtx.Context = ctx
userCtx.User = user
metadata.Protocol = "shadowsocks"
metadata.Destination = destination
return s.handler.NewConnection(&userCtx, &serverConn{
Service: s.Service,
Conn: conn,
uPSK: uPSK,
reader: reader,
requestSalt: requestSalt,
}, metadata)
}
func (s *MultiService[U]) NewPacket(conn socks.PacketConn, buffer *buf.Buffer, metadata M.Metadata) error {
packetHeader := buffer.To(aes.BlockSize)
s.udpBlockCipher.Decrypt(packetHeader, packetHeader)
var _eiHeader [aes.BlockSize]byte
eiHeader := common.Dup(_eiHeader[:])
s.udpBlockCipher.Decrypt(eiHeader, buffer.Range(aes.BlockSize, 2*aes.BlockSize))
for i := range eiHeader {
eiHeader[i] = eiHeader[i] ^ packetHeader[i]
}
var user U
var uPSK [KeySaltSize]byte
if u, loaded := s.uPSKHashR[_eiHeader]; loaded {
user = u
uPSK = s.uPSK[u]
} else {
return E.New("invalid request")
}
var sessionId, packetId uint64
err := binary.Read(buffer, binary.BigEndian, &sessionId)
if err != nil {
return err
}
err = binary.Read(buffer, binary.BigEndian, &packetId)
if err != nil {
return err
}
session, loaded := s.sessions.LoadOrStore(sessionId, func() *serverUDPSession {
return s.newUDPSession(uPSK)
})
if !loaded {
session.remoteSessionId = sessionId
key := Blake3DeriveKey(uPSK[:], packetHeader[:8], s.keyLength)
session.remoteCipher = s.constructor(common.Dup(key))
}
goto process
returnErr:
if !loaded {
s.sessions.Delete(sessionId)
}
return err
process:
if !session.filter.ValidateCounter(packetId, math.MaxUint64) {
err = ErrPacketIdNotUnique
goto returnErr
}
if packetHeader != nil {
_, err = session.remoteCipher.Open(buffer.Index(0), packetHeader[4:16], buffer.Bytes(), nil)
if err != nil {
err = E.Cause(err, "decrypt packet")
goto returnErr
}
}
var headerType byte
headerType, err = buffer.ReadByte()
if err != nil {
err = E.Cause(err, "decrypt packet")
goto returnErr
}
if headerType != HeaderTypeClient {
err = ErrBadHeaderType
goto returnErr
}
var epoch uint64
err = binary.Read(buffer, binary.BigEndian, &epoch)
if err != nil {
goto returnErr
}
if math.Abs(float64(uint64(time.Now().Unix())-epoch)) > 30 {
err = ErrBadTimestamp
goto returnErr
}
var paddingLength uint16
err = binary.Read(buffer, binary.BigEndian, &paddingLength)
if err != nil {
err = E.Cause(err, "read padding length")
goto returnErr
}
buffer.Advance(int(paddingLength))
destination, err := socks.AddressSerializer.ReadAddrPort(buffer)
if err != nil {
goto returnErr
}
metadata.Destination = destination
session.remoteAddr = metadata.Source
var userCtx shadowsocks.UserContext[U]
userCtx.Context = context.Background()
userCtx.User = user
s.udpNat.NewContextPacket(&userCtx, sessionId, func() socks.PacketWriter {
return &serverPacketWriter{s.Service, conn, session}
}, buffer, metadata)
return nil
}
func (m *MultiService[U]) newUDPSession(uPSK [KeySaltSize]byte) *serverUDPSession {
session := &serverUDPSession{}
common.Must(binary.Read(m.secureRNG, binary.BigEndian, &session.sessionId))
session.packetId--
sessionId := make([]byte, 8)
binary.BigEndian.PutUint64(sessionId, session.sessionId)
key := Blake3DeriveKey(uPSK[:], sessionId, m.keyLength)
session.cipher = m.constructor(common.Dup(key))
return session
}