github.com/database64128/shadowsocks-go@v1.7.0/ss2022/packet.go (about)

     1  package ss2022
     2  
     3  import (
     4  	"crypto/cipher"
     5  	"crypto/rand"
     6  	"crypto/subtle"
     7  	"encoding/binary"
     8  	"fmt"
     9  	"math"
    10  	"net/netip"
    11  	"time"
    12  
    13  	"github.com/database64128/shadowsocks-go/conn"
    14  	"github.com/database64128/shadowsocks-go/magic"
    15  	"github.com/database64128/shadowsocks-go/socks5"
    16  	"github.com/database64128/shadowsocks-go/zerocopy"
    17  )
    18  
    19  type ShadowPacketReplayError struct {
    20  	// Source address.
    21  	srcAddr netip.AddrPort
    22  
    23  	// Session ID.
    24  	sid uint64
    25  
    26  	// Packet ID.
    27  	pid uint64
    28  }
    29  
    30  func (e *ShadowPacketReplayError) Unwrap() error {
    31  	return ErrReplay
    32  }
    33  
    34  func (e *ShadowPacketReplayError) Error() string {
    35  	return fmt.Sprintf("received replay packet from %s: session ID %d, packet ID %d", e.srcAddr, e.sid, e.pid)
    36  }
    37  
    38  // ShadowPacketClientMessageHeadroom returns the headroom required by an encrypted Shadowsocks client message.
    39  func ShadowPacketClientMessageHeadroom(identityHeadersLen int) zerocopy.Headroom {
    40  	return zerocopy.Headroom{
    41  		Front: UDPSeparateHeaderLength + identityHeadersLen + UDPClientMessageHeaderMaxLength,
    42  		Rear:  16,
    43  	}
    44  }
    45  
    46  // ShadowPacketServerMessageHeadroom is the headroom required by an encrypted Shadowsocks server message.
    47  var ShadowPacketServerMessageHeadroom = zerocopy.Headroom{
    48  	Front: UDPSeparateHeaderLength + UDPServerMessageHeaderMaxLength,
    49  	Rear:  16,
    50  }
    51  
    52  // ShadowPacketClientPacker packs UDP packets into authenticated and encrypted
    53  // Shadowsocks packets.
    54  //
    55  // ShadowPacketClientPacker implements the zerocopy.Packer interface.
    56  //
    57  // Packet format:
    58  //
    59  //	+---------------------------+-----+-----+---------------------------+
    60  //	| encrypted separate header | EIH | ... |       encrypted body      |
    61  //	+---------------------------+-----+-----+---------------------------+
    62  //	|            16B            | 16B | ... | variable length + 16B tag |
    63  //	+---------------------------+-----+-----+---------------------------+
    64  type ShadowPacketClientPacker struct {
    65  	// Client session ID.
    66  	csid uint64
    67  
    68  	// Client packet ID.
    69  	cpid uint64
    70  
    71  	// Body AEAD cipher.
    72  	aead cipher.AEAD
    73  
    74  	// Block cipher for the separate header.
    75  	block cipher.Block
    76  
    77  	// Padding policy.
    78  	shouldPad PaddingPolicy
    79  
    80  	// EIH block ciphers.
    81  	// Must include a cipher for each iPSK.
    82  	// Must have the same length as eihPSKHashes.
    83  	eihCiphers []cipher.Block
    84  
    85  	// EIH PSK hashes.
    86  	// These are first 16 bytes of BLAKE3 hashes of iPSK1 all the way up to uPSK.
    87  	// Must have the same length as eihCiphers.
    88  	eihPSKHashes [][IdentityHeaderLength]byte
    89  
    90  	// maxPacketSize is the maximum allowed size of a packed packet.
    91  	// The value is calculated from MTU and server address family.
    92  	maxPacketSize int
    93  
    94  	// nonAEADHeaderLen is the length of the separate header and identity headers.
    95  	nonAEADHeaderLen int
    96  
    97  	// info is the client packer info.
    98  	info zerocopy.ClientPackerInfo
    99  
   100  	// serverAddrPort is the Shadowsocks server's address.
   101  	serverAddrPort netip.AddrPort
   102  }
   103  
   104  // ClientPackerInfo implements the zerocopy.ClientPacker ClientPackerInfo method.
   105  func (p *ShadowPacketClientPacker) ClientPackerInfo() zerocopy.ClientPackerInfo {
   106  	return p.info
   107  }
   108  
   109  // PackInPlace implements the zerocopy.ClientPacker PackInPlace method.
   110  func (p *ShadowPacketClientPacker) PackInPlace(b []byte, targetAddr conn.Addr, payloadStart, payloadLen int) (destAddrPort netip.AddrPort, packetStart, packetLen int, err error) {
   111  	targetAddrLen := socks5.LengthOfAddrFromConnAddr(targetAddr)
   112  	headerNoPaddingLen := p.nonAEADHeaderLen + UDPClientMessageHeaderFixedLength + targetAddrLen
   113  	maxPaddingLen := p.maxPacketSize - headerNoPaddingLen - payloadLen - p.aead.Overhead()
   114  	if mpl := payloadStart - headerNoPaddingLen; mpl < maxPaddingLen {
   115  		maxPaddingLen = mpl
   116  	}
   117  	if maxPaddingLen > math.MaxUint16 {
   118  		maxPaddingLen = math.MaxUint16
   119  	}
   120  
   121  	var paddingLen int
   122  
   123  	switch {
   124  	case maxPaddingLen < 0:
   125  		err = zerocopy.ErrPayloadTooBig
   126  		return
   127  	case maxPaddingLen > 0 && p.shouldPad(targetAddr):
   128  		paddingLen = 1 + int(magic.Fastrandn(uint32(maxPaddingLen)))
   129  	}
   130  
   131  	messageHeaderStart := payloadStart - UDPClientMessageHeaderFixedLength - targetAddrLen - paddingLen
   132  
   133  	// Write message header.
   134  	WriteUDPClientMessageHeader(b[messageHeaderStart:payloadStart], paddingLen, targetAddr)
   135  
   136  	destAddrPort = p.serverAddrPort
   137  	packetStart = messageHeaderStart - p.nonAEADHeaderLen
   138  	packetLen = payloadStart - packetStart + payloadLen + p.aead.Overhead()
   139  	identityHeadersStart := packetStart + UDPSeparateHeaderLength
   140  	separateHeader := b[packetStart:identityHeadersStart]
   141  	nonce := separateHeader[4:16]
   142  	plaintext := b[messageHeaderStart : payloadStart+payloadLen]
   143  
   144  	// Write separate header.
   145  	WriteSessionIDAndPacketID(separateHeader, p.csid, p.cpid)
   146  	p.cpid++
   147  
   148  	// Write and encrypt identity headers.
   149  	for i := range p.eihCiphers {
   150  		start := identityHeadersStart + i*IdentityHeaderLength
   151  		identityHeader := b[start : start+IdentityHeaderLength]
   152  		subtle.XORBytes(identityHeader, p.eihPSKHashes[i][:], separateHeader)
   153  		p.eihCiphers[i].Encrypt(identityHeader, identityHeader)
   154  	}
   155  
   156  	// AEAD seal.
   157  	p.aead.Seal(plaintext[:0], nonce, plaintext, nil)
   158  
   159  	// Block encrypt.
   160  	p.block.Encrypt(separateHeader, separateHeader)
   161  
   162  	return
   163  }
   164  
   165  // ShadowPacketServerPacker packs UDP packets into authenticated and encrypted
   166  // Shadowsocks packets.
   167  //
   168  // ShadowPacketServerPacker implements the zerocopy.Packer interface.
   169  type ShadowPacketServerPacker struct {
   170  	// Server session ID.
   171  	ssid uint64
   172  
   173  	// Server packet ID.
   174  	spid uint64
   175  
   176  	// Client session ID.
   177  	csid uint64
   178  
   179  	// Body AEAD cipher.
   180  	aead cipher.AEAD
   181  
   182  	// Block cipher for the separate header.
   183  	block cipher.Block
   184  
   185  	// Padding policy.
   186  	shouldPad PaddingPolicy
   187  }
   188  
   189  // ServerPackerInfo implements the zerocopy.ServerPacker ServerPackerInfo method.
   190  func (p *ShadowPacketServerPacker) ServerPackerInfo() zerocopy.ServerPackerInfo {
   191  	return zerocopy.ServerPackerInfo{
   192  		Headroom: ShadowPacketServerMessageHeadroom,
   193  	}
   194  }
   195  
   196  // PackInPlace implements the zerocopy.ServerPacker PackInPlace method.
   197  func (p *ShadowPacketServerPacker) PackInPlace(b []byte, sourceAddrPort netip.AddrPort, payloadStart, payloadLen, maxPacketLen int) (packetStart, packetLen int, err error) {
   198  	sourceAddrLen := socks5.LengthOfAddrFromAddrPort(sourceAddrPort)
   199  	headerNoPaddingLen := UDPSeparateHeaderLength + UDPServerMessageHeaderFixedLength + sourceAddrLen
   200  	maxPaddingLen := maxPacketLen - headerNoPaddingLen - payloadLen - p.aead.Overhead()
   201  	if mpl := payloadStart - headerNoPaddingLen; mpl < maxPaddingLen {
   202  		maxPaddingLen = mpl
   203  	}
   204  	if maxPaddingLen > math.MaxUint16 {
   205  		maxPaddingLen = math.MaxUint16
   206  	}
   207  
   208  	var paddingLen int
   209  
   210  	switch {
   211  	case maxPaddingLen < 0:
   212  		err = zerocopy.ErrPayloadTooBig
   213  		return
   214  	case maxPaddingLen > 0 && p.shouldPad(conn.AddrFromIPPort(sourceAddrPort)):
   215  		paddingLen = 1 + int(magic.Fastrandn(uint32(maxPaddingLen)))
   216  	}
   217  
   218  	messageHeaderStart := payloadStart - UDPServerMessageHeaderFixedLength - paddingLen - sourceAddrLen
   219  
   220  	// Write message header.
   221  	WriteUDPServerMessageHeader(b[messageHeaderStart:payloadStart], p.csid, paddingLen, sourceAddrPort)
   222  
   223  	packetStart = messageHeaderStart - UDPSeparateHeaderLength
   224  	packetLen = payloadStart - packetStart + payloadLen + p.aead.Overhead()
   225  	separateHeader := b[packetStart:messageHeaderStart]
   226  	nonce := separateHeader[4:16]
   227  	plaintext := b[messageHeaderStart : payloadStart+payloadLen]
   228  
   229  	// Write separate header.
   230  	WriteSessionIDAndPacketID(separateHeader, p.ssid, p.spid)
   231  	p.spid++
   232  
   233  	// AEAD seal.
   234  	p.aead.Seal(plaintext[:0], nonce, plaintext, nil)
   235  
   236  	// Block encrypt.
   237  	p.block.Encrypt(separateHeader, separateHeader)
   238  
   239  	return
   240  }
   241  
   242  // ShadowPacketClientUnpacker unpacks Shadowsocks server packets and returns
   243  // target address and plaintext payload.
   244  //
   245  // When a server session changes, there's a replay window of less than 60 seconds,
   246  // during which an adversary can replay packets with a valid timestamp from the old session.
   247  // To protect against such attacks, and to simplify implementation and save resources,
   248  // we only save information for one previous session.
   249  //
   250  // In an unlikely event where the server session changed more than once within 60s,
   251  // we simply drop new server sessions.
   252  //
   253  // ShadowPacketClientUnpacker implements the zerocopy.Unpacker interface.
   254  type ShadowPacketClientUnpacker struct {
   255  	// Client session ID.
   256  	csid 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 *Filter
   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 *Filter
   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       *Filter
   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 = &Filter{}
   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.SessionServerUnpacker interface.
   392  type ShadowPacketServerUnpacker struct {
   393  	// Client session ID.
   394  	csid uint64
   395  
   396  	// Body AEAD cipher.
   397  	aead cipher.AEAD
   398  
   399  	// Client session sliding window filter.
   400  	//
   401  	// This filter instance should be created during the first successful unpack operation.
   402  	// We trade 2 extra nil checks during unpacking for better performance when the server is flooded by invalid packets.
   403  	filter *Filter
   404  
   405  	// cachedDomain caches the last used domain target to avoid allocating new strings.
   406  	cachedDomain string
   407  
   408  	// nonAEADHeaderLen is the length of the separate header and identity headers.
   409  	nonAEADHeaderLen int
   410  
   411  	// info is the server unpacker info.
   412  	info zerocopy.ServerUnpackerInfo
   413  
   414  	// userCipherConfig is used when creating a new server packer.
   415  	userCipherConfig UserCipherConfig
   416  
   417  	// packerShouldPad is the server packer's padding policy.
   418  	packerShouldPad PaddingPolicy
   419  }
   420  
   421  // ServerUnpackerInfo implements the zerocopy.SessionServerUnpacker ServerUnpackerInfo method.
   422  func (p *ShadowPacketServerUnpacker) ServerUnpackerInfo() zerocopy.ServerUnpackerInfo {
   423  	return p.info
   424  }
   425  
   426  // UnpackInPlace unpacks the AEAD encrypted part of a Shadowsocks client packet
   427  // and returns target address, payload start offset and payload length, or an error.
   428  //
   429  // UnpackInPlace implements the zerocopy.SessionServerUnpacker UnpackInPlace method.
   430  func (p *ShadowPacketServerUnpacker) UnpackInPlace(b []byte, sourceAddr netip.AddrPort, packetStart, packetLen int) (targetAddr conn.Addr, payloadStart, payloadLen int, err error) {
   431  	// Check length.
   432  	if packetLen < p.nonAEADHeaderLen+p.aead.Overhead() {
   433  		err = fmt.Errorf("%w: %d", zerocopy.ErrPacketTooSmall, packetLen)
   434  		return
   435  	}
   436  
   437  	messageHeaderStart := packetStart + p.nonAEADHeaderLen
   438  	separateHeader := b[packetStart : packetStart+UDPSeparateHeaderLength]
   439  	nonce := separateHeader[4:16]
   440  	ciphertext := b[messageHeaderStart : packetStart+packetLen]
   441  
   442  	// Check cpid.
   443  	cpid := binary.BigEndian.Uint64(separateHeader[8:])
   444  	if p.filter != nil && !p.filter.IsOk(cpid) {
   445  		err = &ShadowPacketReplayError{sourceAddr, p.csid, cpid}
   446  		return
   447  	}
   448  
   449  	// AEAD open.
   450  	plaintext, err := p.aead.Open(ciphertext[:0], nonce, ciphertext, nil)
   451  	if err != nil {
   452  		return
   453  	}
   454  
   455  	// Parse message header.
   456  	targetAddr, p.cachedDomain, payloadStart, payloadLen, err = ParseUDPClientMessageHeader(plaintext, p.cachedDomain)
   457  	if err != nil {
   458  		return
   459  	}
   460  	payloadStart += messageHeaderStart
   461  
   462  	// Add cpid to filter.
   463  	if p.filter == nil {
   464  		p.filter = &Filter{}
   465  	}
   466  	p.filter.MustAdd(cpid)
   467  
   468  	return
   469  }
   470  
   471  // NewPacker implements the zerocopy.SessionServerUnpacker NewPacker method.
   472  func (p *ShadowPacketServerUnpacker) NewPacker() (zerocopy.ServerPacker, error) {
   473  	// Random server session ID.
   474  	salt := make([]byte, 8)
   475  	_, err := rand.Read(salt)
   476  	if err != nil {
   477  		return nil, err
   478  	}
   479  	ssid := binary.BigEndian.Uint64(salt)
   480  
   481  	aead, err := p.userCipherConfig.AEAD(salt)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	return &ShadowPacketServerPacker{
   487  		ssid:      ssid,
   488  		csid:      p.csid,
   489  		aead:      aead,
   490  		block:     p.userCipherConfig.Block(),
   491  		shouldPad: p.packerShouldPad,
   492  	}, nil
   493  }