safing-portbase/rng/rng.go
2022-02-01 13:12:46 +01:00

81 lines
1.5 KiB
Go

package rng
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
"sync"
"github.com/aead/serpent"
"github.com/seehuhn/fortuna"
"github.com/safing/portbase/modules"
)
var (
rng *fortuna.Generator
rngLock sync.Mutex
rngReady = false
rngCipher = "aes"
// Possible values: "aes", "serpent".
module *modules.Module
)
func init() {
module = modules.Register("rng", nil, start, nil)
}
func newCipher(key []byte) (cipher.Block, error) {
switch rngCipher {
case "aes":
return aes.NewCipher(key)
case "serpent":
return serpent.NewCipher(key)
default:
return nil, fmt.Errorf("unknown or unsupported cipher: %s", rngCipher)
}
}
func start() error {
rngLock.Lock()
defer rngLock.Unlock()
rng = fortuna.NewGenerator(newCipher)
if rng == nil {
return errors.New("failed to initialize rng")
}
// add another (async) OS rng seed
module.StartWorker("initial rng feed", func(_ context.Context) error {
// get entropy from OS
osEntropy := make([]byte, minFeedEntropy/8)
_, err := rand.Read(osEntropy)
if err != nil {
return fmt.Errorf("could not read entropy from os: %w", err)
}
// feed
rngLock.Lock()
rng.Reseed(osEntropy)
rngLock.Unlock()
return nil
})
// mark as ready
rngReady = true
// random source: OS
module.StartServiceWorker("os rng feeder", 0, osFeeder)
// random source: goroutine ticks
module.StartServiceWorker("tick rng feeder", 0, tickFeeder)
// full feeder
module.StartServiceWorker("full feeder", 0, fullFeeder)
return nil
}