1
0
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:
Darien Raymond 2017-11-26 21:51:30 +01:00
parent 9ec3e40cee
commit fefb5c8e01
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
3 changed files with 171 additions and 45 deletions

View File

@ -103,6 +103,8 @@ type Cipher interface {
NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error)
NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error)
IsAEAD() bool
EncodePacket(key []byte, b *buf.Buffer) error
DecodePacket(key []byte, b *buf.Buffer) error
}
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
}
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 {
KeyBytes int
IVBytes int
@ -149,32 +166,60 @@ func (c *AEADCipher) IVSize() int {
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()
subkey := make([]byte, c.KeyBytes)
hkdfSHA1(key, iv, subkey)
auth := &crypto.AEADAuthenticator{
return &crypto.AEADAuthenticator{
AEAD: c.AEADAuthCreator(subkey),
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{
Auth: auth,
}, writer, protocol.TransferTypeStream), nil
}
func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
nonce := crypto.NewIncreasingAEADNonceGenerator()
subkey := make([]byte, c.KeyBytes)
hkdfSHA1(key, iv, subkey)
auth := &crypto.AEADAuthenticator{
AEAD: c.AEADAuthCreator(subkey),
NonceGenerator: nonce,
}
auth := c.createAuthenticator(key, iv)
return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
Auth: auth,
}, 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 {
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
}
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 {
pwdBytes := []byte(password)
key := make([]byte, 0, keySize)

View File

@ -247,39 +247,31 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff
common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, ivLen)))
iv := buffer.Bytes()
payloadBuffer := buf.New()
defer payloadBuffer.Release()
switch request.Address.Family() {
case net.AddressFamilyIPv4:
payloadBuffer.AppendBytes(AddrTypeIPv4)
payloadBuffer.Append([]byte(request.Address.IP()))
buffer.AppendBytes(AddrTypeIPv4)
buffer.Append([]byte(request.Address.IP()))
case net.AddressFamilyIPv6:
payloadBuffer.AppendBytes(AddrTypeIPv6)
payloadBuffer.Append([]byte(request.Address.IP()))
buffer.AppendBytes(AddrTypeIPv6)
buffer.Append([]byte(request.Address.IP()))
case net.AddressFamilyDomain:
payloadBuffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
payloadBuffer.Append([]byte(request.Address.Domain()))
buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
buffer.Append([]byte(request.Address.Domain()))
default:
return nil, newError("unsupported address type: ", request.Address.Family()).AtError()
}
common.Must(payloadBuffer.AppendSupplier(serial.WriteUint16(uint16(request.Port))))
payloadBuffer.Append(payload)
common.Must(buffer.AppendSupplier(serial.WriteUint16(uint16(request.Port))))
buffer.Append(payload)
if !account.Cipher.IsAEAD() && request.Option.Has(RequestOptionOneTimeAuth) {
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))))
}
w, err := account.Cipher.NewEncryptionWriter(account.Key, iv, buffer)
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()
if err := account.Cipher.EncodePacket(account.Key, buffer); err != nil {
return nil, newError("failed to encrypt UDP payload").Base(err)
}
return buffer, nil
@ -292,24 +284,15 @@ func DecodeUDPPacket(user *protocol.User, payload *buf.Buffer) (*protocol.Reques
}
account := rawAccount.(*ShadowsocksAccount)
ivLen := account.Cipher.IVSize()
iv := make([]byte, ivLen)
copy(iv, payload.BytesTo(ivLen))
payload.SliceFrom(ivLen)
r, err := account.Cipher.NewDecryptionReader(account.Key, iv, payload)
if err != nil {
return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
var authenticator *Authenticator
if !account.Cipher.IsAEAD() {
authenticator = NewAuthenticator(HeaderKeyGenerator(account.Key, payload.BytesTo(account.Cipher.IVSize())))
}
if err := account.Cipher.DecodePacket(account.Key, payload); err != nil {
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
}
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{
Version: Version,
User: user,

View File

@ -10,6 +10,7 @@ import (
"v2ray.com/core"
"v2ray.com/core/app/log"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/protocol"
"v2ray.com/core/common/serial"
@ -690,3 +691,85 @@ func TestShadowsocksAES256GCMConformance(t *testing.T) {
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)
}