github.com/safing/portbase@v0.19.5/rng/rng.go (about) 1 package rng 2 3 import ( 4 "context" 5 "crypto/aes" 6 "crypto/cipher" 7 "crypto/rand" 8 "errors" 9 "fmt" 10 "sync" 11 12 "github.com/aead/serpent" 13 "github.com/seehuhn/fortuna" 14 15 "github.com/safing/portbase/modules" 16 ) 17 18 var ( 19 rng *fortuna.Generator 20 rngLock sync.Mutex 21 rngReady = false 22 23 rngCipher = "aes" 24 // Possible values: "aes", "serpent". 25 26 module *modules.Module 27 ) 28 29 func init() { 30 module = modules.Register("rng", nil, start, nil) 31 } 32 33 func newCipher(key []byte) (cipher.Block, error) { 34 switch rngCipher { 35 case "aes": 36 return aes.NewCipher(key) 37 case "serpent": 38 return serpent.NewCipher(key) 39 default: 40 return nil, fmt.Errorf("unknown or unsupported cipher: %s", rngCipher) 41 } 42 } 43 44 func start() error { 45 rngLock.Lock() 46 defer rngLock.Unlock() 47 48 rng = fortuna.NewGenerator(newCipher) 49 if rng == nil { 50 return errors.New("failed to initialize rng") 51 } 52 53 // add another (async) OS rng seed 54 module.StartWorker("initial rng feed", func(_ context.Context) error { 55 // get entropy from OS 56 osEntropy := make([]byte, minFeedEntropy/8) 57 _, err := rand.Read(osEntropy) 58 if err != nil { 59 return fmt.Errorf("could not read entropy from os: %w", err) 60 } 61 // feed 62 rngLock.Lock() 63 rng.Reseed(osEntropy) 64 rngLock.Unlock() 65 return nil 66 }) 67 68 // mark as ready 69 rngReady = true 70 71 // random source: OS 72 module.StartServiceWorker("os rng feeder", 0, osFeeder) 73 74 // random source: goroutine ticks 75 module.StartServiceWorker("tick rng feeder", 0, tickFeeder) 76 77 // full feeder 78 module.StartServiceWorker("full feeder", 0, fullFeeder) 79 80 return nil 81 }