mirror of https://gogs.blitter.com/RLabs/xs
Add optional cipher/hmac algo remodulate on rekey
This commit is contained in:
parent
e82d968381
commit
32b669192b
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
VERSION := 0.9.6
|
VERSION := 0.9.7
|
||||||
.PHONY: lint vis clean common client server passwd\
|
.PHONY: lint vis clean common client server passwd\
|
||||||
subpkgs install uninstall reinstall scc
|
subpkgs install uninstall reinstall scc
|
||||||
|
|
||||||
|
|
10
xs/xs.go
10
xs/xs.go
|
@ -702,6 +702,7 @@ func main() { //nolint: funlen, gocyclo
|
||||||
cmdStr string
|
cmdStr string
|
||||||
tunSpecStr string // lport1:rport1[,lport2:rport2,...]
|
tunSpecStr string // lport1:rport1[,lport2:rport2,...]
|
||||||
rekeySecs uint
|
rekeySecs uint
|
||||||
|
remodRequested bool // true: when rekeying, switch to random cipher/hmac alg
|
||||||
copySrc []byte
|
copySrc []byte
|
||||||
copyDst string
|
copyDst string
|
||||||
copyQuiet bool
|
copyQuiet bool
|
||||||
|
@ -747,6 +748,7 @@ func main() { //nolint: funlen, gocyclo
|
||||||
flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") //nolint:lll
|
flag.StringVar(&kcpMode, "K", "unused", "KCP `alg`, one of [KCP_NONE | KCP_AES | KCP_BLOWFISH | KCP_CAST5 | KCP_SM4 | KCP_SALSA20 | KCP_SIMPLEXOR | KCP_TEA | KCP_3DES | KCP_TWOFISH | KCP_XTEA] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP") //nolint:lll
|
||||||
flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll
|
flag.UintVar(&port, "p", 2000, "``port") //nolint:gomnd,lll
|
||||||
flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
|
flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
|
||||||
|
flag.BoolVar(&remodRequested, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)")
|
||||||
//nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie")
|
//nolint:gocritic,nolintlint // flag.StringVar(&authCookie, "a", "", "auth cookie")
|
||||||
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
|
flag.BoolVar(&chaffEnabled, "e", true, "enable chaff pkts")
|
||||||
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd
|
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt freq min `msecs`") //nolint:gomnd
|
||||||
|
@ -967,7 +969,13 @@ func main() { //nolint: funlen, gocyclo
|
||||||
if kcpMode != "unused" {
|
if kcpMode != "unused" {
|
||||||
proto = "kcp"
|
proto = "kcp"
|
||||||
}
|
}
|
||||||
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
|
|
||||||
|
remodExtArg := ""
|
||||||
|
if remodRequested {
|
||||||
|
remodExtArg = "OPT_REMOD"
|
||||||
|
}
|
||||||
|
// Pass opt to Dial() via extensions arg
|
||||||
|
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode, remodExtArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
exitWithStatus(XSNetDialFailed)
|
exitWithStatus(XSNetDialFailed)
|
||||||
|
|
18
xsd/xsd.go
18
xsd/xsd.go
|
@ -530,11 +530,13 @@ func main() { //nolint:funlen,gocyclo
|
||||||
var dbg bool
|
var dbg bool
|
||||||
var laddr string
|
var laddr string
|
||||||
var rekeySecs uint
|
var rekeySecs uint
|
||||||
|
var remodSupported bool // true: when rekeying, switch to random cipher/hmac alg
|
||||||
|
|
||||||
var useSystemPasswd bool
|
var useSystemPasswd bool
|
||||||
|
|
||||||
flag.BoolVar(&vopt, "v", false, "show version")
|
flag.BoolVar(&vopt, "v", false, "show version")
|
||||||
flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
|
flag.UintVar(&rekeySecs, "r", 300, "rekey interval in `secs`")
|
||||||
|
flag.BoolVar(&remodSupported, "R", false, "Borg Countermeasures (remodulate cipher/hmac alg on each rekey)")
|
||||||
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") //nolint:gomnd,lll
|
flag.StringVar(&laddr, "l", ":2000", "interface[:port] to listen") //nolint:gomnd,lll
|
||||||
flag.StringVar(&kcpMode, "K", "unused", `set to one of ["KCP_NONE","KCP_AES", "KCP_BLOWFISH", "KCP_CAST5", "KCP_SM4", "KCP_SALSA20", "KCP_SIMPLEXOR", "KCP_TEA", "KCP_3DES", "KCP_TWOFISH", "KCP_XTEA"] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP`) //nolint:lll
|
flag.StringVar(&kcpMode, "K", "unused", `set to one of ["KCP_NONE","KCP_AES", "KCP_BLOWFISH", "KCP_CAST5", "KCP_SM4", "KCP_SALSA20", "KCP_SIMPLEXOR", "KCP_TEA", "KCP_3DES", "KCP_TWOFISH", "KCP_XTEA"] to use KCP (github.com/xtaci/kcp-go) reliable UDP instead of TCP`) //nolint:lll
|
||||||
flag.BoolVar(&useSysLogin, "L", false, "use system login")
|
flag.BoolVar(&useSysLogin, "L", false, "use system login")
|
||||||
|
@ -702,6 +704,22 @@ func main() { //nolint:funlen,gocyclo
|
||||||
} else {
|
} else {
|
||||||
log.Println("Accepted client")
|
log.Println("Accepted client")
|
||||||
|
|
||||||
|
// Only enable cipher alg changes on re-key if we were told
|
||||||
|
// to support it (launching xsd with -R), *and* the client
|
||||||
|
// proposes to use it.
|
||||||
|
if !remodSupported {
|
||||||
|
if (conn.Opts() & xsnet.CORemodulateShields) != 0 {
|
||||||
|
logger.LogDebug("[client proposed cipher/hmac remod, but we don't support it.]")
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if conn.Opts()&xsnet.CORemodulateShields != 0 {
|
||||||
|
logger.LogDebug("[cipher/hmac remodulation active]")
|
||||||
|
} else {
|
||||||
|
logger.LogDebug("[cipher/hmac remodulation inactive]")
|
||||||
|
}
|
||||||
|
}
|
||||||
conn.RekeyHelper(rekeySecs)
|
conn.RekeyHelper(rekeySecs)
|
||||||
|
|
||||||
// Set up chaffing to client
|
// Set up chaffing to client
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"blitter.com/go/cryptmt"
|
"blitter.com/go/cryptmt"
|
||||||
"blitter.com/go/hopscotch"
|
"blitter.com/go/hopscotch"
|
||||||
|
"blitter.com/go/xs/logger"
|
||||||
"github.com/aead/chacha20/chacha"
|
"github.com/aead/chacha20/chacha"
|
||||||
"golang.org/x/crypto/blowfish"
|
"golang.org/x/crypto/blowfish"
|
||||||
"golang.org/x/crypto/twofish"
|
"golang.org/x/crypto/twofish"
|
||||||
|
@ -57,9 +58,19 @@ func expandKeyMat(keymat []byte, blocksize int) []byte {
|
||||||
return keymat
|
return keymat
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning
|
// Choose a cipher and hmac alg from supported sets, given two uint8 values
|
||||||
a cipherStream and hash
|
func getNewStreamAlgs(cb uint8, hb uint8) (config uint32) {
|
||||||
*/
|
// Get new cipher and hash algs (clamped to valid values) based on
|
||||||
|
// the input rekeying data
|
||||||
|
c := (cb % CAlgNoneDisallowed)
|
||||||
|
h := (hb % HmacNoneDisallowed)
|
||||||
|
config = uint32(h<<8) | uint32(c)
|
||||||
|
logger.LogDebug(fmt.Sprintf("[Chose new algs [%d:%d]", h, c))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Re-)initialize the keystream and hmac state for an xsnet.Conn, returning
|
||||||
|
// a cipherStream and hash
|
||||||
func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
|
func (hc *Conn) getStream(keymat []byte) (rc cipher.Stream, mc hash.Hash, err error) {
|
||||||
var key []byte
|
var key []byte
|
||||||
var block cipher.Block
|
var block cipher.Block
|
||||||
|
|
|
@ -122,5 +122,19 @@ const (
|
||||||
HmacNoneDisallowed
|
HmacNoneDisallowed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Conn opts outside of basic kex/cipher/hmac connect config
|
||||||
|
const (
|
||||||
|
CONone = iota
|
||||||
|
CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg
|
||||||
|
)
|
||||||
|
|
||||||
|
type COValue uint32
|
||||||
|
|
||||||
// Available HMACs for hkex.Conn
|
// Available HMACs for hkex.Conn
|
||||||
type CSHmacAlg uint32
|
type CSHmacAlg uint32
|
||||||
|
|
||||||
|
// Some bounds-checking consts
|
||||||
|
const (
|
||||||
|
REKEY_SECS_MIN = 1
|
||||||
|
CHAFF_FREQ_MSECS_MIN = 1
|
||||||
|
)
|
||||||
|
|
49
xsnet/net.go
49
xsnet/net.go
|
@ -241,7 +241,7 @@ func (hc *Conn) SetConnOpts(copts uint32) {
|
||||||
//
|
//
|
||||||
// Consumers of this lib may use this for protocol-level options not part
|
// Consumers of this lib may use this for protocol-level options not part
|
||||||
// of the KEx or encryption info used by the connection.
|
// of the KEx or encryption info used by the connection.
|
||||||
func (hc Conn) Opts() uint32 {
|
func (hc *Conn) Opts() uint32 {
|
||||||
return hc.opts
|
return hc.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,6 +363,9 @@ func (hc *Conn) applyConnExtensions(extensions ...string) {
|
||||||
log.Println("[extension arg = H_SHA512]")
|
log.Println("[extension arg = H_SHA512]")
|
||||||
hc.cipheropts &= (0xFFFF00FF)
|
hc.cipheropts &= (0xFFFF00FF)
|
||||||
hc.cipheropts |= (HmacSHA512 << 8)
|
hc.cipheropts |= (HmacSHA512 << 8)
|
||||||
|
case "OPT_REMOD":
|
||||||
|
log.Println("[extension arg = OPT_REMOD]")
|
||||||
|
hc.opts |= CORemodulateShields
|
||||||
//default:
|
//default:
|
||||||
// log.Printf("[Dial ext \"%s\" ignored]\n", s)
|
// log.Printf("[Dial ext \"%s\" ignored]\n", s)
|
||||||
}
|
}
|
||||||
|
@ -1351,6 +1354,11 @@ func (hc *Conn) Read(b []byte) (n int, err error) {
|
||||||
//logger.LogDebug(fmt.Sprintf("[Got rekey [%02x %02x %02x ...]\n",
|
//logger.LogDebug(fmt.Sprintf("[Got rekey [%02x %02x %02x ...]\n",
|
||||||
// payloadBytes[0], payloadBytes[1], payloadBytes[2]))
|
// payloadBytes[0], payloadBytes[1], payloadBytes[2]))
|
||||||
rekeyData := payloadBytes
|
rekeyData := payloadBytes
|
||||||
|
if (hc.opts & CORemodulateShields) != 0 {
|
||||||
|
hc.Lock()
|
||||||
|
hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1])
|
||||||
|
hc.Unlock()
|
||||||
|
}
|
||||||
hc.r, hc.rm, err = hc.getStream(rekeyData)
|
hc.r, hc.rm, err = hc.getStream(rekeyData)
|
||||||
case CSOTermSize:
|
case CSOTermSize:
|
||||||
fmt.Sscanf(string(payloadBytes), "%d %d", &hc.Rows, &hc.Cols)
|
fmt.Sscanf(string(payloadBytes), "%d %d", &hc.Rows, &hc.Cols)
|
||||||
|
@ -1585,7 +1593,9 @@ func (hc *Conn) StartupChaff() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *Conn) ShutdownChaff() {
|
func (hc *Conn) ShutdownChaff() {
|
||||||
|
hc.Lock()
|
||||||
hc.chaff.shutdown = true
|
hc.chaff.shutdown = true
|
||||||
|
hc.Unlock()
|
||||||
log.Println("Chaffing SHUTDOWN")
|
log.Println("Chaffing SHUTDOWN")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1596,16 +1606,28 @@ func (hc *Conn) SetupChaff(msecsMin uint, msecsMax uint, szMax uint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *Conn) ShutdownRekey() {
|
func (hc *Conn) ShutdownRekey() {
|
||||||
|
hc.Lock()
|
||||||
hc.rekey = 0
|
hc.rekey = 0
|
||||||
|
hc.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *Conn) RekeyHelper(intervalSecs uint) {
|
func (hc *Conn) RekeyHelper(intervalSecs uint) {
|
||||||
|
if intervalSecs < REKEY_SECS_MIN {
|
||||||
|
intervalSecs = REKEY_SECS_MIN
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
hc.Lock()
|
||||||
hc.rekey = intervalSecs
|
hc.rekey = intervalSecs
|
||||||
|
hc.Unlock()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if hc.rekey != 0 {
|
hc.Lock()
|
||||||
|
rekey := hc.rekey
|
||||||
|
hc.Unlock()
|
||||||
|
if rekey != 0 {
|
||||||
//logger.LogDebug(fmt.Sprintf("[rekeyHelper Loop]\n"))
|
//logger.LogDebug(fmt.Sprintf("[rekeyHelper Loop]\n"))
|
||||||
time.Sleep(time.Duration(hc.rekey) * time.Second)
|
time.Sleep(time.Duration(rekey) * time.Second)
|
||||||
|
|
||||||
// Send rekey to other end
|
// Send rekey to other end
|
||||||
rekeyData := make([]byte, 64)
|
rekeyData := make([]byte, 64)
|
||||||
|
@ -1615,6 +1637,9 @@ func (hc *Conn) RekeyHelper(intervalSecs uint) {
|
||||||
//logger.LogDebug("[+rekeyHelper]")
|
//logger.LogDebug("[+rekeyHelper]")
|
||||||
_, err = hc.WritePacket(rekeyData, CSORekey)
|
_, err = hc.WritePacket(rekeyData, CSORekey)
|
||||||
hc.Lock()
|
hc.Lock()
|
||||||
|
if (hc.opts & CORemodulateShields) != 0 {
|
||||||
|
hc.cipheropts = getNewStreamAlgs(rekeyData[0], rekeyData[1])
|
||||||
|
}
|
||||||
hc.w, hc.wm, err = hc.getStream(rekeyData)
|
hc.w, hc.wm, err = hc.getStream(rekeyData)
|
||||||
//logger.LogDebug("[-rekeyHelper]")
|
//logger.LogDebug("[-rekeyHelper]")
|
||||||
hc.Unlock()
|
hc.Unlock()
|
||||||
|
@ -1631,11 +1656,21 @@ func (hc *Conn) RekeyHelper(intervalSecs uint) {
|
||||||
|
|
||||||
// Helper routine to spawn a chaffing goroutine for each Conn
|
// Helper routine to spawn a chaffing goroutine for each Conn
|
||||||
func (hc *Conn) chaffHelper() {
|
func (hc *Conn) chaffHelper() {
|
||||||
|
// Enforce bounds on chaff frequency and pkt size
|
||||||
|
hc.Lock()
|
||||||
|
if hc.chaff.msecsMin < CHAFF_FREQ_MSECS_MIN {
|
||||||
|
hc.chaff.msecsMin = CHAFF_FREQ_MSECS_MIN
|
||||||
|
}
|
||||||
|
hc.Unlock()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
var nextDuration int
|
var nextDuration int
|
||||||
for {
|
for {
|
||||||
//logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n"))
|
//logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n"))
|
||||||
if !hc.chaff.shutdown {
|
hc.Lock()
|
||||||
|
shutdown := hc.chaff.shutdown
|
||||||
|
hc.Unlock()
|
||||||
|
if !shutdown {
|
||||||
var bufTmp []byte
|
var bufTmp []byte
|
||||||
bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax)))
|
bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax)))
|
||||||
min := int(hc.chaff.msecsMin)
|
min := int(hc.chaff.msecsMin)
|
||||||
|
@ -1646,7 +1681,9 @@ func (hc *Conn) chaffHelper() {
|
||||||
//logger.LogDebug("[-chaffHelper]")
|
//logger.LogDebug("[-chaffHelper]")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[ *** error - chaffHelper shutting down *** ]")
|
log.Println("[ *** error - chaffHelper shutting down *** ]")
|
||||||
|
hc.Lock()
|
||||||
hc.chaff.shutdown = true
|
hc.chaff.shutdown = true
|
||||||
|
hc.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1670,7 +1707,9 @@ func (hc *Conn) ShutdownKeepAlive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hc *Conn) ResetKeepAlive() {
|
func (hc *Conn) ResetKeepAlive() {
|
||||||
|
hc.Lock()
|
||||||
hc.keepalive = 3
|
hc.keepalive = 3
|
||||||
|
hc.Unlock()
|
||||||
log.Println("KeepAlive RESET")
|
log.Println("KeepAlive RESET")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1689,7 +1728,9 @@ func (hc *Conn) keepaliveHelper() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(nextDuration) * time.Millisecond)
|
time.Sleep(time.Duration(nextDuration) * time.Millisecond)
|
||||||
|
hc.Lock()
|
||||||
hc.keepalive -= 1
|
hc.keepalive -= 1
|
||||||
|
hc.Unlock()
|
||||||
//logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive))
|
//logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive))
|
||||||
|
|
||||||
//if rand.Intn(8) == 0 {
|
//if rand.Intn(8) == 0 {
|
||||||
|
|
Loading…
Reference in New Issue