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 }