diff --git a/README.md b/README.md index 460cf90..00aeb08 100644 --- a/README.md +++ b/README.md @@ -63,16 +63,6 @@ In all releases prior to v0.9.3, absent a specific whitelist of algs to allow, t As of release v0.9.3, the default when supplying no explicit KEX, cipher or HMAC algorithms to `xsd` results in *no* algs being accepted; so the admin must decide on a specific whitelist of algorithms. *** -*** -**Experimental Session Crypto 'Remodulation' on Rekeying** - -Support has been added for an experimental 'remodulation' of the active session cipher and hash algorithms -when a rekey is performed. This feature, only active if the `-R` option is specified by *both* server and -client, will cause the two sides to negotiate a (potentially) new cipher and hash algorithm as part of the rekeying (normal rekeying *without* 'remodulation' is on by default, with the interval optionally specified independently by client and server via the `-r` (note lowercase) option). - -This experimental 'remodulation' is intended to inhibit a potential attacker's efforts to analyze session data, by reducing the usefulness of any distinguisher, presuming one exists, that may exist in one of the symmetric cipher algos used during a session. Note however, that if the initial KEM negotiation were broken and the attacker has recorded all following session traffic, they can still 'stay in sync' with the rekeys *and* algo remodulations, and be able to continue decrypting across session rekeys. (That is, this feature does not -add any security if the initial KEM exchange were broken.) -*** ### Conn Calls to xsnet.Dial() and xsnet.Listen()/Accept() are generally the same as calls to the equivalents within the _net_ package; however upon connection a key exchange automatically occurs whereby client and server independently derive the same keying material, and all following traffic is secured by a symmetric encryption algorithm. diff --git a/xsnet/consts.go b/xsnet/consts.go index ae2740c..87015b3 100644 --- a/xsnet/consts.go +++ b/xsnet/consts.go @@ -124,10 +124,17 @@ const ( // Conn opts outside of basic kex/cipher/hmac connect config const ( - CONone = iota - CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg + CONone = iota + CORemodulateShields // if set, rekeying also reselects random cipher/hmac alg ) + type COValue uint32 // Available HMACs for hkex.Conn type CSHmacAlg uint32 + +// Some bounds-checking consts +const ( + REKEY_SECS_MIN = 1 + CHAFF_FREQ_MSECS_MIN = 1 +) diff --git a/xsnet/net.go b/xsnet/net.go index 945a362..f200c63 100644 --- a/xsnet/net.go +++ b/xsnet/net.go @@ -366,8 +366,8 @@ func (hc *Conn) applyConnExtensions(extensions ...string) { case "OPT_REMOD": log.Println("[extension arg = OPT_REMOD]") hc.opts |= CORemodulateShields - //default: - // log.Printf("[Dial ext \"%s\" ignored]\n", s) + //default: + // log.Printf("[Dial ext \"%s\" ignored]\n", s) } } } @@ -1355,7 +1355,9 @@ func (hc *Conn) Read(b []byte) (n int, err error) { // payloadBytes[0], payloadBytes[1], payloadBytes[2])) 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) case CSOTermSize: @@ -1591,7 +1593,9 @@ func (hc *Conn) StartupChaff() { } func (hc *Conn) ShutdownChaff() { + hc.Lock() hc.chaff.shutdown = true + hc.Unlock() log.Println("Chaffing SHUTDOWN") } @@ -1602,16 +1606,28 @@ func (hc *Conn) SetupChaff(msecsMin uint, msecsMax uint, szMax uint) { } func (hc *Conn) ShutdownRekey() { + hc.Lock() hc.rekey = 0 + hc.Unlock() } func (hc *Conn) RekeyHelper(intervalSecs uint) { + if intervalSecs < REKEY_SECS_MIN { + intervalSecs = REKEY_SECS_MIN + } + go func() { + hc.Lock() hc.rekey = intervalSecs + hc.Unlock() + for { - if hc.rekey != 0 { + hc.Lock() + rekey := hc.rekey + hc.Unlock() + if rekey != 0 { //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 rekeyData := make([]byte, 64) @@ -1640,11 +1656,21 @@ func (hc *Conn) RekeyHelper(intervalSecs uint) { // Helper routine to spawn a chaffing goroutine for each Conn 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() { var nextDuration int for { //logger.LogDebug(fmt.Sprintf("[chaffHelper Loop]\n")) - if !hc.chaff.shutdown { + hc.Lock() + shutdown := hc.chaff.shutdown + hc.Unlock() + if !shutdown { var bufTmp []byte bufTmp = make([]byte, rand.Intn(int(hc.chaff.szMax))) min := int(hc.chaff.msecsMin) @@ -1655,7 +1681,9 @@ func (hc *Conn) chaffHelper() { //logger.LogDebug("[-chaffHelper]") if err != nil { log.Println("[ *** error - chaffHelper shutting down *** ]") + hc.Lock() hc.chaff.shutdown = true + hc.Unlock() break } } else { @@ -1679,7 +1707,9 @@ func (hc *Conn) ShutdownKeepAlive() { } func (hc *Conn) ResetKeepAlive() { + hc.Lock() hc.keepalive = 3 + hc.Unlock() log.Println("KeepAlive RESET") } @@ -1698,7 +1728,9 @@ func (hc *Conn) keepaliveHelper() { break } time.Sleep(time.Duration(nextDuration) * time.Millisecond) + hc.Lock() hc.keepalive -= 1 + hc.Unlock() //logger.LogDebug(fmt.Sprintf("[keepAlive is now %d]\n", hc.keepalive)) //if rand.Intn(8) == 0 {