mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-21 17:46:58 -05:00
fix aead udp
This commit is contained in:
parent
9ec3e40cee
commit
fefb5c8e01
@ -103,6 +103,8 @@ type Cipher interface {
|
|||||||
NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error)
|
NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error)
|
||||||
NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error)
|
NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error)
|
||||||
IsAEAD() bool
|
IsAEAD() bool
|
||||||
|
EncodePacket(key []byte, b *buf.Buffer) error
|
||||||
|
DecodePacket(key []byte, b *buf.Buffer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type AesCfb struct {
|
type AesCfb struct {
|
||||||
@ -131,6 +133,21 @@ func (v *AesCfb) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (b
|
|||||||
return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
|
return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *AesCfb) EncodePacket(key []byte, b *buf.Buffer) error {
|
||||||
|
iv := b.BytesTo(v.IVSize())
|
||||||
|
stream := crypto.NewAesEncryptionStream(key, iv)
|
||||||
|
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *AesCfb) DecodePacket(key []byte, b *buf.Buffer) error {
|
||||||
|
iv := b.BytesTo(v.IVSize())
|
||||||
|
stream := crypto.NewAesEncryptionStream(key, iv)
|
||||||
|
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||||
|
b.SliceFrom(v.IVSize())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type AEADCipher struct {
|
type AEADCipher struct {
|
||||||
KeyBytes int
|
KeyBytes int
|
||||||
IVBytes int
|
IVBytes int
|
||||||
@ -149,32 +166,60 @@ func (c *AEADCipher) IVSize() int {
|
|||||||
return c.IVBytes
|
return c.IVBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AEADCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
|
func (c *AEADCipher) createAuthenticator(key []byte, iv []byte) *crypto.AEADAuthenticator {
|
||||||
nonce := crypto.NewIncreasingAEADNonceGenerator()
|
nonce := crypto.NewIncreasingAEADNonceGenerator()
|
||||||
subkey := make([]byte, c.KeyBytes)
|
subkey := make([]byte, c.KeyBytes)
|
||||||
hkdfSHA1(key, iv, subkey)
|
hkdfSHA1(key, iv, subkey)
|
||||||
auth := &crypto.AEADAuthenticator{
|
return &crypto.AEADAuthenticator{
|
||||||
AEAD: c.AEADAuthCreator(subkey),
|
AEAD: c.AEADAuthCreator(subkey),
|
||||||
NonceGenerator: nonce,
|
NonceGenerator: nonce,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AEADCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
|
||||||
|
auth := c.createAuthenticator(key, iv)
|
||||||
return crypto.NewAuthenticationWriter(auth, &crypto.AEADChunkSizeParser{
|
return crypto.NewAuthenticationWriter(auth, &crypto.AEADChunkSizeParser{
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
}, writer, protocol.TransferTypeStream), nil
|
}, writer, protocol.TransferTypeStream), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
|
func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
|
||||||
nonce := crypto.NewIncreasingAEADNonceGenerator()
|
auth := c.createAuthenticator(key, iv)
|
||||||
subkey := make([]byte, c.KeyBytes)
|
|
||||||
hkdfSHA1(key, iv, subkey)
|
|
||||||
auth := &crypto.AEADAuthenticator{
|
|
||||||
AEAD: c.AEADAuthCreator(subkey),
|
|
||||||
NonceGenerator: nonce,
|
|
||||||
}
|
|
||||||
return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
|
return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
}, reader, protocol.TransferTypeStream), nil
|
}, reader, protocol.TransferTypeStream), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error {
|
||||||
|
ivLen := c.IVSize()
|
||||||
|
payloadLen := b.Len()
|
||||||
|
auth := c.createAuthenticator(key, b.BytesTo(ivLen))
|
||||||
|
return b.Reset(func(bb []byte) (int, error) {
|
||||||
|
bbb, err := auth.Seal(bb[:ivLen], bb[ivLen:payloadLen])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(bbb), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error {
|
||||||
|
ivLen := c.IVSize()
|
||||||
|
payloadLen := b.Len()
|
||||||
|
auth := c.createAuthenticator(key, b.BytesTo(ivLen))
|
||||||
|
if err := b.Reset(func(bb []byte) (int, error) {
|
||||||
|
bbb, err := auth.Open(bb[:ivLen], bb[ivLen:payloadLen])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(bbb), nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.SliceFrom(ivLen)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type ChaCha20 struct {
|
type ChaCha20 struct {
|
||||||
IVBytes int
|
IVBytes int
|
||||||
}
|
}
|
||||||
@ -201,6 +246,21 @@ func (v *ChaCha20) NewDecryptionReader(key []byte, iv []byte, reader io.Reader)
|
|||||||
return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
|
return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *ChaCha20) EncodePacket(key []byte, b *buf.Buffer) error {
|
||||||
|
iv := b.BytesTo(v.IVSize())
|
||||||
|
stream := crypto.NewChaCha20Stream(key, iv)
|
||||||
|
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *ChaCha20) DecodePacket(key []byte, b *buf.Buffer) error {
|
||||||
|
iv := b.BytesTo(v.IVSize())
|
||||||
|
stream := crypto.NewChaCha20Stream(key, iv)
|
||||||
|
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||||
|
b.SliceFrom(v.IVSize())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func PasswordToCipherKey(password string, keySize int) []byte {
|
func PasswordToCipherKey(password string, keySize int) []byte {
|
||||||
pwdBytes := []byte(password)
|
pwdBytes := []byte(password)
|
||||||
key := make([]byte, 0, keySize)
|
key := make([]byte, 0, keySize)
|
||||||
|
@ -247,39 +247,31 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff
|
|||||||
common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, ivLen)))
|
common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, ivLen)))
|
||||||
iv := buffer.Bytes()
|
iv := buffer.Bytes()
|
||||||
|
|
||||||
payloadBuffer := buf.New()
|
|
||||||
defer payloadBuffer.Release()
|
|
||||||
|
|
||||||
switch request.Address.Family() {
|
switch request.Address.Family() {
|
||||||
case net.AddressFamilyIPv4:
|
case net.AddressFamilyIPv4:
|
||||||
payloadBuffer.AppendBytes(AddrTypeIPv4)
|
buffer.AppendBytes(AddrTypeIPv4)
|
||||||
payloadBuffer.Append([]byte(request.Address.IP()))
|
buffer.Append([]byte(request.Address.IP()))
|
||||||
case net.AddressFamilyIPv6:
|
case net.AddressFamilyIPv6:
|
||||||
payloadBuffer.AppendBytes(AddrTypeIPv6)
|
buffer.AppendBytes(AddrTypeIPv6)
|
||||||
payloadBuffer.Append([]byte(request.Address.IP()))
|
buffer.Append([]byte(request.Address.IP()))
|
||||||
case net.AddressFamilyDomain:
|
case net.AddressFamilyDomain:
|
||||||
payloadBuffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
|
buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
|
||||||
payloadBuffer.Append([]byte(request.Address.Domain()))
|
buffer.Append([]byte(request.Address.Domain()))
|
||||||
default:
|
default:
|
||||||
return nil, newError("unsupported address type: ", request.Address.Family()).AtError()
|
return nil, newError("unsupported address type: ", request.Address.Family()).AtError()
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Must(payloadBuffer.AppendSupplier(serial.WriteUint16(uint16(request.Port))))
|
common.Must(buffer.AppendSupplier(serial.WriteUint16(uint16(request.Port))))
|
||||||
payloadBuffer.Append(payload)
|
buffer.Append(payload)
|
||||||
|
|
||||||
if !account.Cipher.IsAEAD() && request.Option.Has(RequestOptionOneTimeAuth) {
|
if !account.Cipher.IsAEAD() && request.Option.Has(RequestOptionOneTimeAuth) {
|
||||||
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
|
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
|
||||||
payloadBuffer.SetByte(0, payloadBuffer.Byte(0)|0x10)
|
buffer.SetByte(ivLen, buffer.Byte(ivLen)|0x10)
|
||||||
|
|
||||||
common.Must(payloadBuffer.AppendSupplier(authenticator.Authenticate(payloadBuffer.Bytes())))
|
common.Must(buffer.AppendSupplier(authenticator.Authenticate(buffer.BytesFrom(ivLen))))
|
||||||
}
|
}
|
||||||
|
if err := account.Cipher.EncodePacket(account.Key, buffer); err != nil {
|
||||||
w, err := account.Cipher.NewEncryptionWriter(account.Key, iv, buffer)
|
return nil, newError("failed to encrypt UDP payload").Base(err)
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create encoding stream").Base(err).AtError()
|
|
||||||
}
|
|
||||||
if err := w.WriteMultiBuffer(buf.NewMultiBufferValue(payloadBuffer)); err != nil {
|
|
||||||
return nil, newError("failed to encrypt UDP payload").Base(err).AtWarning()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer, nil
|
return buffer, nil
|
||||||
@ -292,24 +284,15 @@ func DecodeUDPPacket(user *protocol.User, payload *buf.Buffer) (*protocol.Reques
|
|||||||
}
|
}
|
||||||
account := rawAccount.(*ShadowsocksAccount)
|
account := rawAccount.(*ShadowsocksAccount)
|
||||||
|
|
||||||
ivLen := account.Cipher.IVSize()
|
var authenticator *Authenticator
|
||||||
iv := make([]byte, ivLen)
|
if !account.Cipher.IsAEAD() {
|
||||||
copy(iv, payload.BytesTo(ivLen))
|
authenticator = NewAuthenticator(HeaderKeyGenerator(account.Key, payload.BytesTo(account.Cipher.IVSize())))
|
||||||
payload.SliceFrom(ivLen)
|
}
|
||||||
|
|
||||||
r, err := account.Cipher.NewDecryptionReader(account.Key, iv, payload)
|
if err := account.Cipher.DecodePacket(account.Key, payload); err != nil {
|
||||||
if err != nil {
|
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||||
return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
|
|
||||||
}
|
}
|
||||||
mb, err := r.ReadMultiBuffer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
payload.Release()
|
|
||||||
payload = mb.SplitFirst()
|
|
||||||
mb.Release()
|
|
||||||
|
|
||||||
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
|
|
||||||
request := &protocol.RequestHeader{
|
request := &protocol.RequestHeader{
|
||||||
Version: Version,
|
Version: Version,
|
||||||
User: user,
|
User: user,
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"v2ray.com/core"
|
"v2ray.com/core"
|
||||||
"v2ray.com/core/app/log"
|
"v2ray.com/core/app/log"
|
||||||
"v2ray.com/core/app/proxyman"
|
"v2ray.com/core/app/proxyman"
|
||||||
|
"v2ray.com/core/common/buf"
|
||||||
"v2ray.com/core/common/net"
|
"v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/common/protocol"
|
"v2ray.com/core/common/protocol"
|
||||||
"v2ray.com/core/common/serial"
|
"v2ray.com/core/common/serial"
|
||||||
@ -690,3 +691,85 @@ func TestShadowsocksAES256GCMConformance(t *testing.T) {
|
|||||||
|
|
||||||
CloseAllServers(servers)
|
CloseAllServers(servers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShadowsocksChacha20Poly1305UDPConformance(t *testing.T) {
|
||||||
|
assert := With(t)
|
||||||
|
|
||||||
|
udpServer := udp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err := udpServer.Start()
|
||||||
|
assert(err, IsNil)
|
||||||
|
defer udpServer.Close()
|
||||||
|
|
||||||
|
account := serial.ToTypedMessage(&shadowsocks.Account{
|
||||||
|
Password: "ss-password",
|
||||||
|
CipherType: shadowsocks.CipherType_CHACHA20_POLY1305,
|
||||||
|
})
|
||||||
|
|
||||||
|
serverPort := pickPort()
|
||||||
|
serverConfig := &core.Config{
|
||||||
|
App: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&log.Config{
|
||||||
|
ErrorLogLevel: log.LogLevel_Debug,
|
||||||
|
ErrorLogType: log.LogType_Console,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*proxyman.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortRange: net.SinglePortRange(serverPort),
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{
|
||||||
|
UdpEnabled: true,
|
||||||
|
User: &protocol.User{
|
||||||
|
Account: account,
|
||||||
|
Level: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*proxyman.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := InitializeServerConfigs(serverConfig)
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
cipher, err := ss.PickCipher("CHACHA20-IETF-POLY1305", nil, "ss-password")
|
||||||
|
assert(err, IsNil)
|
||||||
|
conn, err := ss.ListenPacket("udp", ":0", cipher)
|
||||||
|
assert(err, IsNil)
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
|
||||||
|
payload := buf.New()
|
||||||
|
payload.AppendBytes(1, 127, 0, 0, 1)
|
||||||
|
payload.AppendSupplier(serial.WriteUint16(dest.Port.Value()))
|
||||||
|
|
||||||
|
payload.AppendSupplier(buf.ReadFullFrom(rand.Reader, 10))
|
||||||
|
|
||||||
|
nBytes, err := conn.WriteTo(payload.Bytes(), &net.UDPAddr{
|
||||||
|
IP: []byte{127, 0, 0, 1},
|
||||||
|
Port: int(serverPort),
|
||||||
|
})
|
||||||
|
assert(err, IsNil)
|
||||||
|
assert(nBytes, Equals, payload.Len())
|
||||||
|
|
||||||
|
conn.SetReadDeadline(time.Now().Add(time.Second * 10))
|
||||||
|
response := make([]byte, 10240)
|
||||||
|
nBytes, _, err = conn.ReadFrom(response)
|
||||||
|
assert(err, IsNil)
|
||||||
|
assert(response[:7], Equals, payload.BytesTo(7))
|
||||||
|
assert(response[7:nBytes], Equals, xor(payload.BytesFrom(7)))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(conn.Close(), IsNil)
|
||||||
|
|
||||||
|
CloseAllServers(servers)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user