github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/ss2022/packet.go (about)

     1  package ss2022
     2  
     3  import (
     4  	"context"
     5  	"crypto/cipher"
     6  	"crypto/rand"
     7  	"crypto/subtle"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"math"
    11  	mrand "math/rand/v2"
    12  	"net/netip"
    13  	"time"
    14  
    15  	"github.com/database64128/shadowsocks-go/conn"
    16  	"github.com/database64128/shadowsocks-go/socks5"
    17  	"github.com/database64128/shadowsocks-go/zerocopy"
    18  )
    19  
    20  type ShadowPacketReplayError struct {
    21  	// Source address.
    22  	srcAddr netip.AddrPort
    23  
    24  	// Session ID.
    25  	sid uint64
    26  
    27  	// Packet ID.
    28  	pid uint64
    29  }
    30  
    31  func (e *ShadowPacketReplayError) Unwrap() error {
    32  	return ErrReplay
    33  }
    34  
    35  func (e *ShadowPacketReplayError) Error() string {
    36  	return fmt.Sprintf("received replay packet from %s: session ID %d, packet ID %d", e.srcAddr, e.sid, e.pid)
    37  }
    38  
    39  // ShadowPacketClientMessageHeadroom returns the headroom required by an encrypted Shadowsocks client message.
    40  func ShadowPacketClientMessageHeadroom(identityHeadersLen int) zerocopy.Headroom {
    41  	return zerocopy.Headroom{
    42  		Front: UDPSeparateHeaderLength + identityHeadersLen + UDPClientMessageHeaderMaxLength,
    43  		Rear:  16,
    44  	}
    45  }
    46  
    47  // ShadowPacketServerMessageHeadroom is the headroom required by an encrypted Shadowsocks server message.
    48  var ShadowPacketServerMessageHeadroom = zerocopy.Headroom{
    49  	Front: UDPSeparateHeaderLength + UDPServerMessageHeaderMaxLength,
    50  	Rear:  16,
    51  }
    52  
    53  // ShadowPacketClientPacker packs UDP packets into authenticated and encrypted
    54  // Shadowsocks packets.
    55  //
    56  // ShadowPacketClientPacker implements the zerocopy.Packer interface.
    57  //
    58  // Packet format:
    59  //
    60  //	+---------------------------+-----+-----+---------------------------+
    61  //	| encrypted separate header | EIH | ... |       encrypted body      |
    62  //	+---------------------------+-----+-----+---------------------------+
    63  //	|            16B            | 16B | ... | variable length + 16B tag |
    64  //	+---------------------------+-----+-----+---------------------------+
    65  type ShadowPacketClientPacker struct {
    66  	// Client session ID.
    67  	csid uint64
    68  
    69  	// Client packet ID.
    70  	cpid uint64
    71  
    72  	// Body AEAD cipher.
    73  	aead cipher.AEAD
    74  
    75  	// Block cipher for the separate header.
    76  	block cipher.Block
    77  
    78  	// Padding policy.
    79  	shouldPad PaddingPolicy
    80  
    81  	// EIH block ciphers.
    82  	// Must include a cipher for each iPSK.
    83  	// Must have the same length as eihPSKHashes.
    84  	eihCiphers []cipher.Block
    85  
    86  	// EIH PSK hashes.
    87  	// These are first 16 bytes of BLAKE3 hashes of iPSK1 all the way up to uPSK.
    88  	// Must have the same length as eihCiphers.
    89  	eihPSKHashes [][IdentityHeaderLength]byte
    90  
    91  	// maxPacketSize is the maximum allowed size of a packed packet.
    92  	// The value is calculated from MTU and server address family.
    93  	maxPacketSize int
    94  
    95  	// nonAEADHeaderLen is the length of the separate header and identity headers.
    96  	nonAEADHeaderLen int
    97  
    98  	// info is the client packer info.
    99  	info zerocopy.ClientPackerInfo
   100  
   101  	// serverAddrPort is the Shadowsocks server's address.
   102  	serverAddrPort netip.AddrPort
   103  }
   104  
   105  // ClientPackerInfo implements the zerocopy.ClientPacker ClientPackerInfo method.
   106  func (p *ShadowPacketClientPacker) ClientPackerInfo() zerocopy.ClientPackerInfo {
   107  	return p.info
   108  }
   109  
   110  // PackInPlace implements the zerocopy.ClientPacker PackInPlace method.
   111  func (p *ShadowPacketClientPacker) PackInPlace(ctx context.Context, b []byte, targetAddr conn.Addr, payloadStart, payloadLen int) (destAddrPort netip.AddrPort, packetStart, packetLen int, err error) {
   112  	targetAddrLen := socks5.LengthOfAddrFromConnAddr(targetAddr)
   113  	headerNoPaddingLen := p.nonAEADHeaderLen + UDPClientMessageHeaderFixedLength + targetAddrLen
   114  	maxPaddingLen := min(
   115  		p.maxPacketSize-headerNoPaddingLen-payloadLen-p.aead.Overhead(),
   116  		payloadStart-headerNoPaddingLen,
   117  		math.MaxUint16,
   118  	)
   119  
   120  	var paddingLen int
   121  
   122  	switch {
   123  	case maxPaddingLen < 0:
   124  		err = zerocopy.ErrPayloadTooBig
   125  		return
   126  	case maxPaddingLen > 0 && p.shouldPad(targetAddr):
   127  		paddingLen = 1 + mrand.IntN(maxPaddingLen)
   128  	}
   129  
   130  	messageHeaderStart := payloadStart - UDPClientMessageHeaderFixedLength - targetAddrLen - paddingLen
   131  
   132  	// Write message header.
   133  	WriteUDPClientMessageHeader(b[messageHeaderStart:payloadStart], paddingLen, targetAddr)
   134  
   135  	destAddrPort = p.serverAddrPort
   136  	packetStart = messageHeaderStart - p.nonAEADHeaderLen
   137  	packetLen = payloadStart - packetStart + payloadLen + p.aead.Overhead()
   138  	identityHeadersStart := packetStart + UDPSeparateHeaderLength
   139  	separateHeader := b[packetStart:identityHeadersStart]
   140  	nonce := separateHeader[4:16]
   141  	plaintext := b[messageHeaderStart : payloadStart+payloadLen]
   142  
   143  	// Write separate header.
   144  	WriteSessionIDAndPacketID(separateHeader, p.csid, p.cpid)
   145  	p.cpid++
   146  
   147  	// Write and encrypt identity headers.
   148  	for i := range p.eihCiphers {
   149  		start := identityHeadersStart + i*IdentityHeaderLength
   150  		identityHeader := b[start : start+IdentityHeaderLength]
   151  		subtle.XORBytes(identityHeader, p.eihPSKHashes[i][:], separateHeader)
   152  		p.eihCiphers[i].Encrypt(identityHeader, identityHeader)
   153  	}
   154  
   155  	// AEAD seal.
   156  	p.aead.Seal(plaintext[:0], nonce, plaintext, nil)
   157  
   158  	// Block encrypt.
   159  	p.block.Encrypt(separateHeader, separateHeader)
   160  
   161  	return
   162  }
   163  
   164  // ShadowPacketServerPacker packs UDP packets into authenticated and encrypted
   165  // Shadowsocks packets.
   166  //
   167  // ShadowPacketServerPacker implements the zerocopy.Packer interface.
   168  type ShadowPacketServerPacker struct {
   169  	// Server session ID.
   170  	ssid uint64
   171  
   172  	// Server packet ID.
   173  	spid uint64
   174  
   175  	// Client session ID.
   176  	csid uint64
   177  
   178  	// Body AEAD cipher.
   179  	aead cipher.AEAD
   180  
   181  	// Block cipher for the separate header.
   182  	block cipher.Block
   183  
   184  	// Padding policy.
   185  	shouldPad PaddingPolicy
   186  }
   187  
   188  // ServerPackerInfo implements the zerocopy.ServerPacker ServerPackerInfo method.
   189  func (p *ShadowPacketServerPacker) ServerPackerInfo() zerocopy.ServerPackerInfo {
   190  	return zerocopy.ServerPackerInfo{
   191  		Headroom: ShadowPacketServerMessageHeadroom,
   192  	}
   193  }
   194  
   195  // PackInPlace implements the zerocopy.ServerPacker PackInPlace method.
   196  func (p *ShadowPacketServerPacker) PackInPlace(b []byte, sourceAddrPort netip.AddrPort, payloadStart, payloadLen, maxPacketLen int) (packetStart, packetLen int, err error) {
   197  	sourceAddrLen := socks5.LengthOfAddrFromAddrPort(sourceAddrPort)
   198  	headerNoPaddingLen := UDPSeparateHeaderLength + UDPServerMessageHeaderFixedLength + sourceAddrLen
   199  	maxPaddingLen := min(
   200  		maxPacketLen-headerNoPaddingLen-payloadLen-p.aead.Overhead(),
   201  		payloadStart-headerNoPaddingLen,
   202  		math.MaxUint16,
   203  	)
   204  
   205  	var paddingLen int
   206  
   207  	switch {
   208  	case maxPaddingLen < 0:
   209  		err = zerocopy.ErrPayloadTooBig
   210  		return
   211  	case maxPaddingLen > 0 && p.shouldPad(conn.AddrFromIPPort(sourceAddrPort)):
   212  		paddingLen = 1 + mrand.IntN(maxPaddingLen)
   213  	}
   214  
   215  	messageHeaderStart := payloadStart - UDPServerMessageHeaderFixedLength - paddingLen - sourceAddrLen
   216  
   217  	// Write message header.
   218  	WriteUDPServerMessageHeader(b[messageHeaderStart:payloadStart], p.csid, paddingLen, sourceAddrPort)
   219  
   220  	packetStart = messageHeaderStart - UDPSeparateHeaderLength
   221  	packetLen = payloadStart - packetStart + payloadLen + p.aead.Overhead()
   222  	separateHeader := b[packetStart:messageHeaderStart]
   223  	nonce := separateHeader[4:16]
   224  	plaintext := b[messageHeaderStart : payloadStart+payloadLen]
   225  
   226  	// Write separate header.
   227  	WriteSessionIDAndPacketID(separateHeader, p.ssid, p.spid)
   228  	p.spid++
   229  
   230  	// AEAD seal.
   231  	p.aead.Seal(plaintext[:0], nonce, plaintext, nil)
   232  
   233  	// Block encrypt.
   234  	p.block.Encrypt(separateHeader, separateHeader)
   235  
   236  	return
   237  }
   238  
   239  // ShadowPacketClientUnpacker unpacks Shadowsocks server packets and returns
   240  // target address and plaintext payload.
   241  //
   242  // When a server session changes, there's a replay window of less than 60 seconds,
   243  // during which an adversary can replay packets with a valid timestamp from the old session.
   244  // To protect against such attacks, and to simplify implementation and save resources,
   245  // we only save information for one previous session.
   246  //
   247  // In an unlikely event where the server session changed more than once within 60s,
   248  // we simply drop new server sessions.
   249  //
   250  // ShadowPacketClientUnpacker implements the zerocopy.Unpacker interface.
   251  type ShadowPacketClientUnpacker struct {
   252  	// Client session ID.
   253  	csid uint64
   254  
   255  	// filterSize is the size of the sliding window filter.
   256  	filterSize uint64
   257  
   258  	// Current server session ID.
   259  	currentServerSessionID uint64
   260  
   261  	// Current server session AEAD cipher.
   262  	currentServerSessionAEAD cipher.AEAD
   263  
   264  	// Current server session sliding window filter.
   265  	currentServerSessionFilter *SlidingWindowFilter
   266  
   267  	// Old server session ID.
   268  	oldServerSessionID uint64
   269  
   270  	// Old server session AEAD cipher.
   271  	oldServerSessionAEAD cipher.AEAD
   272  
   273  	// Old server session sliding window filter.
   274  	oldServerSessionFilter *SlidingWindowFilter
   275  
   276  	// Old server session last seen time.
   277  	oldServerSessionLastSeenTime time.Time
   278  
   279  	// Cipher config.
   280  	cipherConfig *ClientCipherConfig
   281  }
   282  
   283  // ClientUnpackerInfo implements the zerocopy.ClientUnpacker ClientUnpackerInfo method.
   284  func (p *ShadowPacketClientUnpacker) ClientUnpackerInfo() zerocopy.ClientUnpackerInfo {
   285  	return zerocopy.ClientUnpackerInfo{
   286  		Headroom: ShadowPacketServerMessageHeadroom,
   287  	}
   288  }
   289  
   290  // UnpackInPlace implements the zerocopy.ClientUnpacker UnpackInPlace method.
   291  func (p *ShadowPacketClientUnpacker) UnpackInPlace(b []byte, packetSourceAddrPort netip.AddrPort, packetStart, packetLen int) (payloadSourceAddrPort netip.AddrPort, payloadStart, payloadLen int, err error) {
   292  	const (
   293  		currentServerSession = iota
   294  		oldServerSession
   295  		newServerSession
   296  	)
   297  
   298  	var (
   299  		ssid          uint64
   300  		spid          uint64
   301  		saead         cipher.AEAD
   302  		sfilter       *SlidingWindowFilter
   303  		sessionStatus int
   304  	)
   305  
   306  	// Check length.
   307  	if packetLen < UDPSeparateHeaderLength+16 {
   308  		err = fmt.Errorf("%w: %d", zerocopy.ErrPacketTooSmall, packetLen)
   309  		return
   310  	}
   311  
   312  	messageHeaderStart := packetStart + UDPSeparateHeaderLength
   313  	separateHeader := b[packetStart:messageHeaderStart]
   314  	nonce := separateHeader[4:16]
   315  	ciphertext := b[messageHeaderStart : packetStart+packetLen]
   316  
   317  	// Decrypt separate header.
   318  	p.cipherConfig.Block().Decrypt(separateHeader, separateHeader)
   319  
   320  	// Determine session status.
   321  	ssid = binary.BigEndian.Uint64(separateHeader)
   322  	spid = binary.BigEndian.Uint64(separateHeader[8:])
   323  	switch {
   324  	case ssid == p.currentServerSessionID && p.currentServerSessionAEAD != nil:
   325  		saead = p.currentServerSessionAEAD
   326  		sfilter = p.currentServerSessionFilter
   327  		sessionStatus = currentServerSession
   328  	case ssid == p.oldServerSessionID && p.oldServerSessionAEAD != nil:
   329  		saead = p.oldServerSessionAEAD
   330  		sfilter = p.oldServerSessionFilter
   331  		sessionStatus = oldServerSession
   332  	case time.Since(p.oldServerSessionLastSeenTime) < time.Minute:
   333  		// Reject fast-changing server sessions.
   334  		err = ErrTooManyServerSessions
   335  		return
   336  	default:
   337  		// Likely a new server session.
   338  		// Delay sfilter creation after validation to avoid a possibly unnecessary allocation.
   339  		saead, err = p.cipherConfig.AEAD(separateHeader[:8])
   340  		if err != nil {
   341  			return
   342  		}
   343  		sessionStatus = newServerSession
   344  	}
   345  
   346  	// Check spid.
   347  	if sfilter != nil && !sfilter.IsOk(spid) {
   348  		err = &ShadowPacketReplayError{packetSourceAddrPort, ssid, spid}
   349  		return
   350  	}
   351  
   352  	// AEAD open.
   353  	plaintext, err := saead.Open(ciphertext[:0], nonce, ciphertext, nil)
   354  	if err != nil {
   355  		return
   356  	}
   357  
   358  	// Parse message header.
   359  	payloadSourceAddrPort, payloadStart, payloadLen, err = ParseUDPServerMessageHeader(plaintext, p.csid)
   360  	if err != nil {
   361  		return
   362  	}
   363  	payloadStart += messageHeaderStart
   364  
   365  	// Add spid to filter.
   366  	if sessionStatus == newServerSession {
   367  		sfilter = NewSlidingWindowFilter(p.filterSize)
   368  	}
   369  	sfilter.MustAdd(spid)
   370  
   371  	// Update session status.
   372  	switch sessionStatus {
   373  	case oldServerSession:
   374  		p.oldServerSessionLastSeenTime = time.Now()
   375  	case newServerSession:
   376  		p.oldServerSessionID = p.currentServerSessionID
   377  		p.oldServerSessionAEAD = p.currentServerSessionAEAD
   378  		p.oldServerSessionFilter = p.currentServerSessionFilter
   379  		p.oldServerSessionLastSeenTime = time.Now()
   380  		p.currentServerSessionID = ssid
   381  		p.currentServerSessionAEAD = saead
   382  		p.currentServerSessionFilter = sfilter
   383  	}
   384  
   385  	return
   386  }
   387  
   388  // ShadowPacketServerUnpacker unpacks Shadowsocks client packets and returns
   389  // target address and plaintext payload.
   390  //
   391  // ShadowPacketServerUnpacker implements the zerocopy.ServerUnpacker interface.
   392  type ShadowPacketServerUnpacker struct {
   393  	// Client session ID.
   394  	csid uint64
   395  
   396  	// Body AEAD cipher.
   397  	aead cipher.AEAD
   398  
   399  	// filterSize is the size of the sliding window filter.
   400  	filterSize uint64
   401  
   402  	// Client session sliding window filter.
   403  	//
   404  	// This filter instance should be created during the first successful unpack operation.
   405  	// We trade 2 extra nil checks during unpacking for better performance when the server is flooded by invalid packets.
   406  	filter *SlidingWindowFilter
   407  
   408  	// cachedDomain caches the last used domain target to avoid allocating new strings.
   409  	cachedDomain string
   410  
   411  	// nonAEADHeaderLen is the length of the separate header and identity headers.
   412  	nonAEADHeaderLen int
   413  
   414  	// info is the server unpacker info.
   415  	info zerocopy.ServerUnpackerInfo
   416  
   417  	// userCipherConfig is used when creating a new server packer.
   418  	userCipherConfig UserCipherConfig
   419  
   420  	// packerShouldPad is the server packer's padding policy.
   421  	packerShouldPad PaddingPolicy
   422  }
   423  
   424  // ServerUnpackerInfo implements the zerocopy.ServerUnpacker ServerUnpackerInfo method.
   425  func (p *ShadowPacketServerUnpacker) ServerUnpackerInfo() zerocopy.ServerUnpackerInfo {
   426  	return p.info
   427  }
   428  
   429  // UnpackInPlace unpacks the AEAD encrypted part of a Shadowsocks client packet
   430  // and returns target address, payload start offset and payload length, or an error.
   431  //
   432  // UnpackInPlace implements the zerocopy.ServerUnpacker UnpackInPlace method.
   433  func (p *ShadowPacketServerUnpacker) UnpackInPlace(b []byte, sourceAddr netip.AddrPort, packetStart, packetLen int) (targetAddr conn.Addr, payloadStart, payloadLen int, err error) {
   434  	// Check length.
   435  	if packetLen < p.nonAEADHeaderLen+p.aead.Overhead() {
   436  		err = fmt.Errorf("%w: %d", zerocopy.ErrPacketTooSmall, packetLen)
   437  		return
   438  	}
   439  
   440  	messageHeaderStart := packetStart + p.nonAEADHeaderLen
   441  	separateHeader := b[packetStart : packetStart+UDPSeparateHeaderLength]
   442  	nonce := separateHeader[4:16]
   443  	ciphertext := b[messageHeaderStart : packetStart+packetLen]
   444  
   445  	// Check cpid.
   446  	cpid := binary.BigEndian.Uint64(separateHeader[8:])
   447  	if p.filter != nil && !p.filter.IsOk(cpid) {
   448  		err = &ShadowPacketReplayError{sourceAddr, p.csid, cpid}
   449  		return
   450  	}
   451  
   452  	// AEAD open.
   453  	plaintext, err := p.aead.Open(ciphertext[:0], nonce, ciphertext, nil)
   454  	if err != nil {
   455  		return
   456  	}
   457  
   458  	// Parse message header.
   459  	targetAddr, p.cachedDomain, payloadStart, payloadLen, err = ParseUDPClientMessageHeader(plaintext, p.cachedDomain)
   460  	if err != nil {
   461  		return
   462  	}
   463  	payloadStart += messageHeaderStart
   464  
   465  	// Add cpid to filter.
   466  	if p.filter == nil {
   467  		p.filter = NewSlidingWindowFilter(p.filterSize)
   468  	}
   469  	p.filter.MustAdd(cpid)
   470  
   471  	return
   472  }
   473  
   474  // NewPacker implements the zerocopy.ServerUnpacker NewPacker method.
   475  func (p *ShadowPacketServerUnpacker) NewPacker() (zerocopy.ServerPacker, error) {
   476  	// Random server session ID.
   477  	salt := make([]byte, 8)
   478  	_, err := rand.Read(salt)
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  	ssid := binary.BigEndian.Uint64(salt)
   483  
   484  	aead, err := p.userCipherConfig.AEAD(salt)
   485  	if err != nil {
   486  		return nil, err
   487  	}
   488  
   489  	return &ShadowPacketServerPacker{
   490  		ssid:      ssid,
   491  		csid:      p.csid,
   492  		aead:      aead,
   493  		block:     p.userCipherConfig.Block(),
   494  		shouldPad: p.packerShouldPad,
   495  	}, nil
   496  }