mirror of
https://github.com/cmz0228/hysteria-dev.git
synced 2025-06-08 21:39:53 +00:00
feat: salamander obfs
This commit is contained in:
parent
baee5689c1
commit
4c24edaac1
@ -1,5 +1,10 @@
|
|||||||
server: example.com
|
server: example.com
|
||||||
|
|
||||||
|
# obfs:
|
||||||
|
# type: salamander
|
||||||
|
# salamander:
|
||||||
|
# password: some_password
|
||||||
|
|
||||||
auth: some_password
|
auth: some_password
|
||||||
|
|
||||||
# tls:
|
# tls:
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/apernet/hysteria/app/internal/http"
|
"github.com/apernet/hysteria/app/internal/http"
|
||||||
"github.com/apernet/hysteria/app/internal/socks5"
|
"github.com/apernet/hysteria/app/internal/socks5"
|
||||||
"github.com/apernet/hysteria/core/client"
|
"github.com/apernet/hysteria/core/client"
|
||||||
|
"github.com/apernet/hysteria/extras/obfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var clientCmd = &cobra.Command{
|
var clientCmd = &cobra.Command{
|
||||||
@ -32,7 +33,13 @@ func init() {
|
|||||||
type clientConfig struct {
|
type clientConfig struct {
|
||||||
Server string `mapstructure:"server"`
|
Server string `mapstructure:"server"`
|
||||||
Auth string `mapstructure:"auth"`
|
Auth string `mapstructure:"auth"`
|
||||||
TLS struct {
|
Obfs struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
Salamander struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
} `mapstructure:"salamander"`
|
||||||
|
} `mapstructure:"obfs"`
|
||||||
|
TLS struct {
|
||||||
SNI string `mapstructure:"sni"`
|
SNI string `mapstructure:"sni"`
|
||||||
Insecure bool `mapstructure:"insecure"`
|
Insecure bool `mapstructure:"insecure"`
|
||||||
CA string `mapstructure:"ca"`
|
CA string `mapstructure:"ca"`
|
||||||
@ -80,6 +87,24 @@ type forwardingEntry struct {
|
|||||||
// Config validates the fields and returns a ready-to-use Hysteria client config
|
// Config validates the fields and returns a ready-to-use Hysteria client config
|
||||||
func (c *clientConfig) Config() (*client.Config, error) {
|
func (c *clientConfig) Config() (*client.Config, error) {
|
||||||
hyConfig := &client.Config{}
|
hyConfig := &client.Config{}
|
||||||
|
// ConnFactory
|
||||||
|
switch strings.ToLower(c.Obfs.Type) {
|
||||||
|
case "", "plain":
|
||||||
|
// Default, do nothing
|
||||||
|
case "salamander":
|
||||||
|
ob, err := obfs.NewSalamanderObfuscator([]byte(c.Obfs.Salamander.Password))
|
||||||
|
if err != nil {
|
||||||
|
return nil, configError{Field: "obfs.salamander.password", Err: err}
|
||||||
|
}
|
||||||
|
hyConfig.ConnFactory = &obfsConnFactory{
|
||||||
|
NewFunc: func(addr net.Addr) (net.PacketConn, error) {
|
||||||
|
return net.ListenUDP("udp", nil)
|
||||||
|
},
|
||||||
|
Obfuscator: ob,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, configError{Field: "obfs.type", Err: errors.New("unsupported obfuscation type")}
|
||||||
|
}
|
||||||
// ServerAddr
|
// ServerAddr
|
||||||
if c.Server == "" {
|
if c.Server == "" {
|
||||||
return nil, configError{Field: "server", Err: errors.New("server address is empty")}
|
return nil, configError{Field: "server", Err: errors.New("server address is empty")}
|
||||||
@ -311,6 +336,20 @@ func parseServerAddrString(addrStr string) (host, hostPort string) {
|
|||||||
return h, addrStr
|
return h, addrStr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// obfsConnFactory adds obfuscation to a function that creates net.PacketConn.
|
||||||
|
type obfsConnFactory struct {
|
||||||
|
NewFunc func(addr net.Addr) (net.PacketConn, error)
|
||||||
|
Obfuscator obfs.Obfuscator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *obfsConnFactory) New(addr net.Addr) (net.PacketConn, error) {
|
||||||
|
conn, err := f.NewFunc(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obfs.WrapPacketConn(conn, f.Obfuscator), nil
|
||||||
|
}
|
||||||
|
|
||||||
type socks5Logger struct{}
|
type socks5Logger struct{}
|
||||||
|
|
||||||
func (l *socks5Logger) TCPRequest(addr net.Addr, reqAddr string) {
|
func (l *socks5Logger) TCPRequest(addr net.Addr, reqAddr string) {
|
||||||
|
@ -22,6 +22,19 @@ func TestClientConfig(t *testing.T) {
|
|||||||
if !reflect.DeepEqual(config, clientConfig{
|
if !reflect.DeepEqual(config, clientConfig{
|
||||||
Server: "example.com",
|
Server: "example.com",
|
||||||
Auth: "weak_ahh_password",
|
Auth: "weak_ahh_password",
|
||||||
|
Obfs: struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
Salamander struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
} `mapstructure:"salamander"`
|
||||||
|
}{
|
||||||
|
Type: "salamander",
|
||||||
|
Salamander: struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
}{
|
||||||
|
Password: "cry_me_a_r1ver",
|
||||||
|
},
|
||||||
|
},
|
||||||
TLS: struct {
|
TLS: struct {
|
||||||
SNI string `mapstructure:"sni"`
|
SNI string `mapstructure:"sni"`
|
||||||
Insecure bool `mapstructure:"insecure"`
|
Insecure bool `mapstructure:"insecure"`
|
||||||
|
@ -2,6 +2,11 @@ server: example.com
|
|||||||
|
|
||||||
auth: weak_ahh_password
|
auth: weak_ahh_password
|
||||||
|
|
||||||
|
obfs:
|
||||||
|
type: salamander
|
||||||
|
salamander:
|
||||||
|
password: cry_me_a_r1ver
|
||||||
|
|
||||||
tls:
|
tls:
|
||||||
sni: another.example.com
|
sni: another.example.com
|
||||||
insecure: true
|
insecure: true
|
||||||
|
@ -11,13 +11,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apernet/hysteria/core/server"
|
|
||||||
"github.com/apernet/hysteria/extras/auth"
|
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/apernet/hysteria/core/server"
|
||||||
|
"github.com/apernet/hysteria/extras/auth"
|
||||||
|
"github.com/apernet/hysteria/extras/obfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var serverCmd = &cobra.Command{
|
var serverCmd = &cobra.Command{
|
||||||
@ -31,10 +32,16 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type serverConfig struct {
|
type serverConfig struct {
|
||||||
Listen string `mapstructure:"listen"`
|
Listen string `mapstructure:"listen"`
|
||||||
TLS *serverConfigTLS `mapstructure:"tls"`
|
Obfs struct {
|
||||||
ACME *serverConfigACME `mapstructure:"acme"`
|
Type string `mapstructure:"type"`
|
||||||
QUIC struct {
|
Salamander struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
} `mapstructure:"salamander"`
|
||||||
|
} `mapstructure:"obfs"`
|
||||||
|
TLS *serverConfigTLS `mapstructure:"tls"`
|
||||||
|
ACME *serverConfigACME `mapstructure:"acme"`
|
||||||
|
QUIC struct {
|
||||||
InitStreamReceiveWindow uint64 `mapstructure:"initStreamReceiveWindow"`
|
InitStreamReceiveWindow uint64 `mapstructure:"initStreamReceiveWindow"`
|
||||||
MaxStreamReceiveWindow uint64 `mapstructure:"maxStreamReceiveWindow"`
|
MaxStreamReceiveWindow uint64 `mapstructure:"maxStreamReceiveWindow"`
|
||||||
InitConnectionReceiveWindow uint64 `mapstructure:"initConnReceiveWindow"`
|
InitConnectionReceiveWindow uint64 `mapstructure:"initConnReceiveWindow"`
|
||||||
@ -92,10 +99,22 @@ func (c *serverConfig) Config() (*server.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, configError{Field: "listen", Err: err}
|
return nil, configError{Field: "listen", Err: err}
|
||||||
}
|
}
|
||||||
hyConfig.Conn, err = net.ListenUDP("udp", uAddr)
|
conn, err := net.ListenUDP("udp", uAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, configError{Field: "listen", Err: err}
|
return nil, configError{Field: "listen", Err: err}
|
||||||
}
|
}
|
||||||
|
switch strings.ToLower(c.Obfs.Type) {
|
||||||
|
case "", "plain":
|
||||||
|
hyConfig.Conn = conn
|
||||||
|
case "salamander":
|
||||||
|
ob, err := obfs.NewSalamanderObfuscator([]byte(c.Obfs.Salamander.Password))
|
||||||
|
if err != nil {
|
||||||
|
return nil, configError{Field: "obfs.salamander.password", Err: err}
|
||||||
|
}
|
||||||
|
hyConfig.Conn = obfs.WrapPacketConn(conn, ob)
|
||||||
|
default:
|
||||||
|
return nil, configError{Field: "obfs.type", Err: errors.New("unsupported obfuscation type")}
|
||||||
|
}
|
||||||
// TLSConfig
|
// TLSConfig
|
||||||
if c.TLS == nil && c.ACME == nil {
|
if c.TLS == nil && c.ACME == nil {
|
||||||
return nil, configError{Field: "tls", Err: errors.New("must set either tls or acme")}
|
return nil, configError{Field: "tls", Err: errors.New("must set either tls or acme")}
|
||||||
|
@ -21,6 +21,19 @@ func TestServerConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
if !reflect.DeepEqual(config, serverConfig{
|
if !reflect.DeepEqual(config, serverConfig{
|
||||||
Listen: ":8443",
|
Listen: ":8443",
|
||||||
|
Obfs: struct {
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
|
Salamander struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
} `mapstructure:"salamander"`
|
||||||
|
}{
|
||||||
|
Type: "salamander",
|
||||||
|
Salamander: struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
}{
|
||||||
|
Password: "cry_me_a_r1ver",
|
||||||
|
},
|
||||||
|
},
|
||||||
TLS: &serverConfigTLS{
|
TLS: &serverConfigTLS{
|
||||||
Cert: "some.crt",
|
Cert: "some.crt",
|
||||||
Key: "some.key",
|
Key: "some.key",
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
listen: :8443
|
listen: :8443
|
||||||
|
|
||||||
|
obfs:
|
||||||
|
type: salamander
|
||||||
|
salamander:
|
||||||
|
password: cry_me_a_r1ver
|
||||||
|
|
||||||
tls:
|
tls:
|
||||||
cert: some.crt
|
cert: some.crt
|
||||||
key: some.key
|
key: some.key
|
||||||
|
@ -159,6 +159,7 @@ func (s *Server) handleConnect(conn net.Conn, req *http.Request) {
|
|||||||
rConn, err := s.HyClient.DialTCP(reqAddr)
|
rConn, err := s.HyClient.DialTCP(reqAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = sendSimpleResponse(conn, req, http.StatusBadGateway)
|
_ = sendSimpleResponse(conn, req, http.StatusBadGateway)
|
||||||
|
closeErr = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer rConn.Close()
|
defer rConn.Close()
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
listen: :443
|
listen: :443
|
||||||
|
|
||||||
|
# obfs:
|
||||||
|
# type: salamander
|
||||||
|
# salamander:
|
||||||
|
# password: some_password
|
||||||
|
|
||||||
# tls:
|
# tls:
|
||||||
# cert: my.crt
|
# cert: my.crt
|
||||||
# key: my.key
|
# key: my.key
|
||||||
|
121
extras/obfs/conn.go
Normal file
121
extras/obfs/conn.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package obfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const udpBufSize = 2048 // QUIC packets are at most 1500 bytes long, so 2k should be more than enough
|
||||||
|
|
||||||
|
// Obfuscator is the interface that wraps the Obfuscate and Deobfuscate methods.
|
||||||
|
// Both methods return the number of bytes written to out.
|
||||||
|
// If a packet is not valid, the methods should return 0.
|
||||||
|
type Obfuscator interface {
|
||||||
|
Obfuscate(in, out []byte) int
|
||||||
|
Deobfuscate(in, out []byte) int
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ net.PacketConn = (*obfsPacketConn)(nil)
|
||||||
|
|
||||||
|
type obfsPacketConn struct {
|
||||||
|
Conn net.PacketConn
|
||||||
|
Obfs Obfuscator
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// obfsPacketConnUDP is a special case of obfsPacketConn that uses a UDPConn
|
||||||
|
// as the underlying connection. We pass additional methods to quic-go to
|
||||||
|
// enable UDP-specific optimizations.
|
||||||
|
type obfsPacketConnUDP struct {
|
||||||
|
*obfsPacketConn
|
||||||
|
UDPConn *net.UDPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapPacketConn enables obfuscation on a net.PacketConn.
|
||||||
|
// The obfuscation is transparent to the caller - the n bytes returned by
|
||||||
|
// ReadFrom and WriteTo are the number of original bytes, not after
|
||||||
|
// obfuscation/deobfuscation.
|
||||||
|
func WrapPacketConn(conn net.PacketConn, obfs Obfuscator) net.PacketConn {
|
||||||
|
opc := &obfsPacketConn{
|
||||||
|
Conn: conn,
|
||||||
|
Obfs: obfs,
|
||||||
|
readBuf: make([]byte, udpBufSize),
|
||||||
|
writeBuf: make([]byte, udpBufSize),
|
||||||
|
}
|
||||||
|
if udpConn, ok := conn.(*net.UDPConn); ok {
|
||||||
|
return &obfsPacketConnUDP{
|
||||||
|
obfsPacketConn: opc,
|
||||||
|
UDPConn: udpConn,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return opc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
for {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
n, addr, err = c.Conn.ReadFrom(c.readBuf)
|
||||||
|
if n <= 0 {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n = c.Obfs.Deobfuscate(c.readBuf[:n], p)
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
if n > 0 || err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Invalid packet, try again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
nn := c.Obfs.Obfuscate(p, c.writeBuf)
|
||||||
|
_, err = c.Conn.WriteTo(c.writeBuf[:nn], addr)
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
if err == nil {
|
||||||
|
n = len(p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) Close() error {
|
||||||
|
return c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) LocalAddr() net.Addr {
|
||||||
|
return c.Conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.Conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.Conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.Conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDP-specific methods below
|
||||||
|
|
||||||
|
func (c *obfsPacketConnUDP) SetReadBuffer(bytes int) error {
|
||||||
|
return c.UDPConn.SetReadBuffer(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConnUDP) SetWriteBuffer(bytes int) error {
|
||||||
|
return c.UDPConn.SetWriteBuffer(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConnUDP) SyscallConn() (syscall.RawConn, error) {
|
||||||
|
return c.UDPConn.SyscallConn()
|
||||||
|
}
|
71
extras/obfs/salamander.go
Normal file
71
extras/obfs/salamander.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package obfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/blake2b"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
smPSKMinLen = 4
|
||||||
|
smSaltLen = 8
|
||||||
|
smKeyLen = blake2b.Size256
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Obfuscator = (*SalamanderObfuscator)(nil)
|
||||||
|
|
||||||
|
var ErrPSKTooShort = fmt.Errorf("PSK must be at least %d bytes", smPSKMinLen)
|
||||||
|
|
||||||
|
// SalamanderObfuscator is an obfuscator that obfuscates each packet with
|
||||||
|
// the BLAKE2b-256 hash of a pre-shared key combined with a random salt.
|
||||||
|
// Packet format: [8-byte salt][payload]
|
||||||
|
type SalamanderObfuscator struct {
|
||||||
|
PSK []byte
|
||||||
|
RandSrc *rand.Rand
|
||||||
|
|
||||||
|
lk sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSalamanderObfuscator(psk []byte) (*SalamanderObfuscator, error) {
|
||||||
|
if len(psk) < smPSKMinLen {
|
||||||
|
return nil, ErrPSKTooShort
|
||||||
|
}
|
||||||
|
return &SalamanderObfuscator{
|
||||||
|
PSK: psk,
|
||||||
|
RandSrc: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *SalamanderObfuscator) Obfuscate(in, out []byte) int {
|
||||||
|
outLen := len(in) + smSaltLen
|
||||||
|
if len(out) < outLen {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
o.lk.Lock()
|
||||||
|
_, _ = o.RandSrc.Read(out[:smSaltLen])
|
||||||
|
o.lk.Unlock()
|
||||||
|
key := o.key(out[:smSaltLen])
|
||||||
|
for i, c := range in {
|
||||||
|
out[i+smSaltLen] = c ^ key[i%smKeyLen]
|
||||||
|
}
|
||||||
|
return outLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *SalamanderObfuscator) Deobfuscate(in, out []byte) int {
|
||||||
|
outLen := len(in) - smSaltLen
|
||||||
|
if outLen <= 0 || len(out) < outLen {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
key := o.key(in[:smSaltLen])
|
||||||
|
for i, c := range in[smSaltLen:] {
|
||||||
|
out[i] = c ^ key[i%smKeyLen]
|
||||||
|
}
|
||||||
|
return outLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *SalamanderObfuscator) key(salt []byte) [smKeyLen]byte {
|
||||||
|
return blake2b.Sum256(append(o.PSK, salt...))
|
||||||
|
}
|
56
extras/obfs/salamander_test.go
Normal file
56
extras/obfs/salamander_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package obfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) {
|
||||||
|
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
in := make([]byte, 1200)
|
||||||
|
_, _ = rand.Read(in)
|
||||||
|
out := make([]byte, 2048)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
o.Obfuscate(in, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSalamanderObfuscator_Deobfuscate(b *testing.B) {
|
||||||
|
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
in := make([]byte, 1200)
|
||||||
|
_, _ = rand.Read(in)
|
||||||
|
out := make([]byte, 2048)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
o.Deobfuscate(in, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSalamanderObfuscator(t *testing.T) {
|
||||||
|
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
in := make([]byte, 1200)
|
||||||
|
oOut := make([]byte, 2048)
|
||||||
|
dOut := make([]byte, 2048)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
_, _ = rand.Read(in)
|
||||||
|
n := o.Obfuscate(in, oOut)
|
||||||
|
if n == 0 {
|
||||||
|
t.Fatal("Failed to obfuscate")
|
||||||
|
}
|
||||||
|
if n != len(in)+smSaltLen {
|
||||||
|
t.Fatal("Wrong obfuscated length")
|
||||||
|
}
|
||||||
|
n = o.Deobfuscate(oOut[:n], dOut)
|
||||||
|
if n == 0 {
|
||||||
|
t.Fatal("Failed to deobfuscate")
|
||||||
|
}
|
||||||
|
if n != len(in) {
|
||||||
|
t.Fatal("Wrong deobfuscated length")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(in, dOut[:n]) {
|
||||||
|
t.Fatal("Deobfuscated data mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user