// standaloneTLSTicketKeyRotation governs over the array of TLS ticket keys used to de/crypt TLS tickets. // It periodically sets a new ticket key as the first one, used to encrypt (and decrypt), // pushing any old ticket keys to the back, where they are considered for decryption only. // // Lack of entropy for the very first ticket key results in the feature being disabled (as does Go), // later lack of entropy temporarily disables ticket key rotation. // Old ticket keys are still phased out, though. // // Stops the ticker when returning. func standaloneTLSTicketKeyRotation(c *tls.Config, ticker *time.Ticker, exitChan chan struct{}) { defer ticker.Stop() // The entire page should be marked as sticky, but Go cannot do that // without resorting to syscall#Mlock. And, we don't have madvise (for NODUMP), too. ☹ keys := make([][32]byte, 1, NumTickets) rng := c.Rand if rng == nil { rng = rand.Reader } if _, err := io.ReadFull(rng, keys[0][:]); err != nil { c.SessionTicketsDisabled = true // bail if we don't have the entropy for the first one return } c.SessionTicketKey = keys[0] // SetSessionTicketKeys doesn't set a 'tls.keysAlreadySet' c.SetSessionTicketKeys(setSessionTicketKeysTestHook(keys)) for { select { case _, isOpen := <-exitChan: if !isOpen { return } case <-ticker.C: rng = c.Rand // could've changed since the start if rng == nil { rng = rand.Reader } var newTicketKey [32]byte _, err := io.ReadFull(rng, newTicketKey[:]) if len(keys) < NumTickets { keys = append(keys, keys[0]) // manipulates the internal length } for idx := len(keys) - 1; idx >= 1; idx-- { keys[idx] = keys[idx-1] // yes, this makes copies } if err == nil { keys[0] = newTicketKey } // pushes the last key out, doesn't matter that we don't have a new one c.SetSessionTicketKeys(setSessionTicketKeysTestHook(keys)) } } }