mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-11-10 06:16:53 -05:00
b6da3e86a5
* Shadowsocks2022 Client Implementation Improvements 1. Added UDP Replay Detection 2. Added UDP Processor State Cache 3. Added More Detailed Output for Time Difference Error 4. Replaced Mutex with RWMutex for reduced lock contention 5. Added per server session tracking of decryption cache and anti-replay window 6. Adjust server session track time 7. Increase per session buffer to 128 8. Fix client crash when EIH is not enabled 9. Fix client log contains non-human-friendly content 10.Remove mark and remove for trackedSessions 11. Fixed packet size uint16 overflow issue
361 lines
8.2 KiB
Go
361 lines
8.2 KiB
Go
package crypto
|
|
|
|
import (
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"io"
|
|
|
|
"github.com/v2fly/v2ray-core/v5/common"
|
|
"github.com/v2fly/v2ray-core/v5/common/buf"
|
|
"github.com/v2fly/v2ray-core/v5/common/bytespool"
|
|
"github.com/v2fly/v2ray-core/v5/common/errors"
|
|
"github.com/v2fly/v2ray-core/v5/common/protocol"
|
|
)
|
|
|
|
type BytesGenerator func() []byte
|
|
|
|
func GenerateEmptyBytes() BytesGenerator {
|
|
var b [1]byte
|
|
return func() []byte {
|
|
return b[:0]
|
|
}
|
|
}
|
|
|
|
func GenerateStaticBytes(content []byte) BytesGenerator {
|
|
return func() []byte {
|
|
return content
|
|
}
|
|
}
|
|
|
|
func GenerateIncreasingNonce(nonce []byte) BytesGenerator {
|
|
c := append([]byte(nil), nonce...)
|
|
return func() []byte {
|
|
for i := range c {
|
|
c[i]++
|
|
if c[i] != 0 {
|
|
break
|
|
}
|
|
}
|
|
return c
|
|
}
|
|
}
|
|
|
|
func GenerateInitialAEADNonce() BytesGenerator {
|
|
return GenerateIncreasingNonce([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
|
|
}
|
|
|
|
type Authenticator interface {
|
|
NonceSize() int
|
|
Overhead() int
|
|
Open(dst, cipherText []byte) ([]byte, error)
|
|
Seal(dst, plainText []byte) ([]byte, error)
|
|
}
|
|
|
|
type AEADAuthenticator struct {
|
|
cipher.AEAD
|
|
NonceGenerator BytesGenerator
|
|
AdditionalDataGenerator BytesGenerator
|
|
}
|
|
|
|
func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) {
|
|
iv := v.NonceGenerator()
|
|
if len(iv) != v.AEAD.NonceSize() {
|
|
return nil, newError("invalid AEAD nonce size: ", len(iv))
|
|
}
|
|
|
|
var additionalData []byte
|
|
if v.AdditionalDataGenerator != nil {
|
|
additionalData = v.AdditionalDataGenerator()
|
|
}
|
|
return v.AEAD.Open(dst, iv, cipherText, additionalData)
|
|
}
|
|
|
|
func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
|
|
iv := v.NonceGenerator()
|
|
if len(iv) != v.AEAD.NonceSize() {
|
|
return nil, newError("invalid AEAD nonce size: ", len(iv))
|
|
}
|
|
|
|
var additionalData []byte
|
|
if v.AdditionalDataGenerator != nil {
|
|
additionalData = v.AdditionalDataGenerator()
|
|
}
|
|
return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
|
|
}
|
|
|
|
type AuthenticationReader struct {
|
|
auth Authenticator
|
|
reader *buf.BufferedReader
|
|
sizeParser ChunkSizeDecoder
|
|
sizeBytes []byte
|
|
transferType protocol.TransferType
|
|
padding PaddingLengthGenerator
|
|
size uint16
|
|
sizeOffset uint16
|
|
paddingLen uint16
|
|
hasSize bool
|
|
done bool
|
|
}
|
|
|
|
func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType, paddingLen PaddingLengthGenerator) *AuthenticationReader {
|
|
r := &AuthenticationReader{
|
|
auth: auth,
|
|
sizeParser: sizeParser,
|
|
transferType: transferType,
|
|
padding: paddingLen,
|
|
sizeBytes: make([]byte, sizeParser.SizeBytes()),
|
|
}
|
|
if chunkSizeDecoderWithOffset, ok := sizeParser.(ChunkSizeDecoderWithOffset); ok {
|
|
r.sizeOffset = chunkSizeDecoderWithOffset.HasConstantOffset()
|
|
}
|
|
if breader, ok := reader.(*buf.BufferedReader); ok {
|
|
r.reader = breader
|
|
} else {
|
|
r.reader = &buf.BufferedReader{Reader: buf.NewReader(reader)}
|
|
}
|
|
return r
|
|
}
|
|
|
|
func (r *AuthenticationReader) readSize() (uint16, uint16, error) {
|
|
if r.hasSize {
|
|
r.hasSize = false
|
|
return r.size, r.paddingLen, nil
|
|
}
|
|
if _, err := io.ReadFull(r.reader, r.sizeBytes); err != nil {
|
|
return 0, 0, err
|
|
}
|
|
var padding uint16
|
|
if r.padding != nil {
|
|
padding = r.padding.NextPaddingLen()
|
|
}
|
|
size, err := r.sizeParser.Decode(r.sizeBytes)
|
|
return size, padding, err
|
|
}
|
|
|
|
var errSoft = newError("waiting for more data")
|
|
|
|
func (r *AuthenticationReader) readBuffer(size int32, padding int32) (*buf.Buffer, error) {
|
|
b := buf.New()
|
|
if _, err := b.ReadFullFrom(r.reader, size); err != nil {
|
|
b.Release()
|
|
return nil, err
|
|
}
|
|
size -= padding
|
|
rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(size))
|
|
if err != nil {
|
|
b.Release()
|
|
return nil, err
|
|
}
|
|
b.Resize(0, int32(len(rb)))
|
|
return b, nil
|
|
}
|
|
|
|
func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) error {
|
|
if soft && r.reader.BufferedBytes() < r.sizeParser.SizeBytes() {
|
|
return errSoft
|
|
}
|
|
|
|
if r.done {
|
|
return io.EOF
|
|
}
|
|
|
|
size, padding, err := r.readSize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if size+r.sizeOffset == uint16(r.auth.Overhead())+padding {
|
|
r.done = true
|
|
return io.EOF
|
|
}
|
|
|
|
effectiveSize := int32(size) + int32(r.sizeOffset)
|
|
|
|
if soft && effectiveSize > r.reader.BufferedBytes() {
|
|
r.size = size
|
|
r.paddingLen = padding
|
|
r.hasSize = true
|
|
return errSoft
|
|
}
|
|
|
|
if size <= buf.Size {
|
|
b, err := r.readBuffer(effectiveSize, int32(padding))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
*mb = append(*mb, b)
|
|
return nil
|
|
}
|
|
|
|
payload := bytespool.Alloc(effectiveSize)
|
|
defer bytespool.Free(payload)
|
|
|
|
if _, err := io.ReadFull(r.reader, payload[:effectiveSize]); err != nil {
|
|
return err
|
|
}
|
|
|
|
effectiveSize -= int32(padding)
|
|
|
|
rb, err := r.auth.Open(payload[:0], payload[:effectiveSize])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*mb = buf.MergeBytes(*mb, rb)
|
|
return nil
|
|
}
|
|
|
|
func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|
const readSize = 16
|
|
mb := make(buf.MultiBuffer, 0, readSize)
|
|
if err := r.readInternal(false, &mb); err != nil {
|
|
buf.ReleaseMulti(mb)
|
|
return nil, err
|
|
}
|
|
|
|
for i := 1; i < readSize; i++ {
|
|
err := r.readInternal(true, &mb)
|
|
if err == errSoft || err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
buf.ReleaseMulti(mb)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return mb, nil
|
|
}
|
|
|
|
type AuthenticationWriter struct {
|
|
auth Authenticator
|
|
writer buf.Writer
|
|
sizeParser ChunkSizeEncoder
|
|
transferType protocol.TransferType
|
|
padding PaddingLengthGenerator
|
|
}
|
|
|
|
func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, writer io.Writer, transferType protocol.TransferType, padding PaddingLengthGenerator) *AuthenticationWriter {
|
|
w := &AuthenticationWriter{
|
|
auth: auth,
|
|
writer: buf.NewWriter(writer),
|
|
sizeParser: sizeParser,
|
|
transferType: transferType,
|
|
}
|
|
if padding != nil {
|
|
w.padding = padding
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *AuthenticationWriter) seal(b []byte) (*buf.Buffer, error) {
|
|
encryptedSize := int32(len(b) + w.auth.Overhead())
|
|
var paddingSize int32
|
|
if w.padding != nil {
|
|
paddingSize = int32(w.padding.NextPaddingLen())
|
|
}
|
|
|
|
sizeBytes := w.sizeParser.SizeBytes()
|
|
totalSize := sizeBytes + encryptedSize + paddingSize
|
|
if totalSize > buf.Size {
|
|
return nil, newError("size too large: ", totalSize)
|
|
}
|
|
|
|
eb := buf.New()
|
|
w.sizeParser.Encode(uint16(encryptedSize+paddingSize), eb.Extend(sizeBytes))
|
|
if _, err := w.auth.Seal(eb.Extend(encryptedSize)[:0], b); err != nil {
|
|
eb.Release()
|
|
return nil, err
|
|
}
|
|
if paddingSize > 0 {
|
|
// These paddings will send in clear text.
|
|
// To avoid leakage of PRNG internal state, a cryptographically secure PRNG should be used.
|
|
paddingBytes := eb.Extend(paddingSize)
|
|
common.Must2(rand.Read(paddingBytes))
|
|
}
|
|
|
|
return eb, nil
|
|
}
|
|
|
|
func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
|
|
defer buf.ReleaseMulti(mb)
|
|
|
|
var maxPadding int32
|
|
if w.padding != nil {
|
|
maxPadding = int32(w.padding.MaxPaddingLen())
|
|
}
|
|
|
|
payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes() - maxPadding
|
|
if len(mb)+10 > 64*1024*1024 {
|
|
return errors.New("value too large")
|
|
}
|
|
sliceSize := len(mb) + 10
|
|
mb2Write := make(buf.MultiBuffer, 0, sliceSize)
|
|
|
|
temp := buf.New()
|
|
defer temp.Release()
|
|
|
|
rawBytes := temp.Extend(payloadSize)
|
|
|
|
for {
|
|
nb, nBytes := buf.SplitBytes(mb, rawBytes)
|
|
mb = nb
|
|
|
|
eb, err := w.seal(rawBytes[:nBytes])
|
|
if err != nil {
|
|
buf.ReleaseMulti(mb2Write)
|
|
return err
|
|
}
|
|
mb2Write = append(mb2Write, eb)
|
|
if mb.IsEmpty() {
|
|
break
|
|
}
|
|
}
|
|
|
|
return w.writer.WriteMultiBuffer(mb2Write)
|
|
}
|
|
|
|
func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
|
|
defer buf.ReleaseMulti(mb)
|
|
|
|
if len(mb)+1 > 64*1024*1024 {
|
|
return errors.New("value too large")
|
|
}
|
|
sliceSize := len(mb) + 1
|
|
mb2Write := make(buf.MultiBuffer, 0, sliceSize)
|
|
|
|
for _, b := range mb {
|
|
if b.IsEmpty() {
|
|
continue
|
|
}
|
|
|
|
eb, err := w.seal(b.Bytes())
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
mb2Write = append(mb2Write, eb)
|
|
}
|
|
|
|
if mb2Write.IsEmpty() {
|
|
return nil
|
|
}
|
|
|
|
return w.writer.WriteMultiBuffer(mb2Write)
|
|
}
|
|
|
|
// WriteMultiBuffer implements buf.Writer.
|
|
func (w *AuthenticationWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|
if mb.IsEmpty() {
|
|
eb, err := w.seal([]byte{})
|
|
common.Must(err)
|
|
return w.writer.WriteMultiBuffer(buf.MultiBuffer{eb})
|
|
}
|
|
|
|
if w.transferType == protocol.TransferTypeStream {
|
|
return w.writeStream(mb)
|
|
}
|
|
|
|
return w.writePacket(mb)
|
|
}
|