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 }