mirror of
https://github.com/safing/portbase
synced 2025-09-01 10:09:50 +00:00
124 lines
3.2 KiB
Go
124 lines
3.2 KiB
Go
package random
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/aead/serpent"
|
|
"github.com/seehuhn/fortuna"
|
|
|
|
"github.com/safing/portbase/config"
|
|
"github.com/safing/portbase/modules"
|
|
)
|
|
|
|
var (
|
|
rng *fortuna.Generator
|
|
rngLock sync.Mutex
|
|
rngReady = false
|
|
rngCipherOption config.StringOption
|
|
|
|
shutdownSignal = make(chan struct{})
|
|
)
|
|
|
|
func init() {
|
|
modules.Register("random", prep, Start, nil, "base")
|
|
}
|
|
|
|
func prep() error {
|
|
err := config.Register(&config.Option{
|
|
Name: "RNG Cipher",
|
|
Key: "random/rng_cipher",
|
|
Description: "Cipher to use for the Fortuna RNG. Requires restart to take effect.",
|
|
OptType: config.OptTypeString,
|
|
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
ExternalOptType: "string list",
|
|
DefaultValue: "aes",
|
|
ValidationRegex: "^(aes|serpent)$",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rngCipherOption = config.GetAsString("random/rng_cipher", "aes")
|
|
|
|
err = config.Register(&config.Option{
|
|
Name: "Minimum Feed Entropy",
|
|
Key: "random/min_feed_entropy",
|
|
Description: "The minimum amount of entropy before a entropy source is feed to the RNG, in bits.",
|
|
OptType: config.OptTypeInt,
|
|
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
DefaultValue: 256,
|
|
ValidationRegex: "^[0-9]{3,5}$",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
minFeedEntropy = config.Concurrent.GetAsInt("random/min_feed_entropy", 256)
|
|
|
|
err = config.Register(&config.Option{
|
|
Name: "Reseed after x seconds",
|
|
Key: "random/reseed_after_seconds",
|
|
Description: "Number of seconds until reseed",
|
|
OptType: config.OptTypeInt,
|
|
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
DefaultValue: 360, // ten minutes
|
|
ValidationRegex: "^[1-9][0-9]{1,5}$",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
reseedAfterSeconds = config.Concurrent.GetAsInt("random/reseed_after_seconds", 360)
|
|
|
|
err = config.Register(&config.Option{
|
|
Name: "Reseed after x bytes",
|
|
Key: "random/reseed_after_bytes",
|
|
Description: "Number of fetched bytes until reseed",
|
|
OptType: config.OptTypeInt,
|
|
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
|
ReleaseLevel: config.ReleaseLevelExperimental,
|
|
DefaultValue: 1000000, // one megabyte
|
|
ValidationRegex: "^[1-9][0-9]{2,9}$",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
reseedAfterBytes = config.GetAsInt("random/reseed_after_bytes", 1000000)
|
|
|
|
return nil
|
|
}
|
|
|
|
func newCipher(key []byte) (cipher.Block, error) {
|
|
cipher := rngCipherOption()
|
|
switch cipher {
|
|
case "aes":
|
|
return aes.NewCipher(key)
|
|
case "serpent":
|
|
return serpent.NewCipher(key)
|
|
default:
|
|
return nil, fmt.Errorf("unknown or unsupported cipher: %s", cipher)
|
|
}
|
|
}
|
|
|
|
// Start starts the RNG. Normally, this should be only called by the portbase/modules package.
|
|
func Start() (err error) {
|
|
rngLock.Lock()
|
|
defer rngLock.Unlock()
|
|
|
|
rng = fortuna.NewGenerator(newCipher)
|
|
rngReady = true
|
|
|
|
// random source: OS
|
|
go osFeeder()
|
|
|
|
// random source: goroutine ticks
|
|
go tickFeeder()
|
|
|
|
// full feeder
|
|
go fullFeeder()
|
|
|
|
return nil
|
|
}
|