github.com/safing/portbase@v0.19.5/rng/tickfeeder.go (about) 1 package rng 2 3 import ( 4 "context" 5 "encoding/binary" 6 "time" 7 ) 8 9 func getTickFeederTickDuration() time.Duration { 10 // be ready in 1/10 time of reseedAfterSeconds 11 msecsAvailable := reseedAfterSeconds * 100 12 // ex.: reseed after 10 minutes: msecsAvailable = 60000 13 // have full entropy after 5 minutes 14 15 // one tick generates 0,125 bits of entropy 16 ticksNeeded := minFeedEntropy * 8 17 // ex.: minimum entropy is 256: ticksNeeded = 2048 18 19 // msces between ticks 20 tickMsecs := msecsAvailable / ticksNeeded 21 // ex.: tickMsecs = 29(,296875) 22 23 // use a minimum of 10 msecs per tick for good entropy 24 // it would take 21 seconds to get full 256 bits of entropy with 10msec ticks 25 if tickMsecs < 10 { 26 tickMsecs = 10 27 } 28 29 return time.Duration(tickMsecs) * time.Millisecond 30 } 31 32 // tickFeeder is a really simple entropy feeder that adds the least significant bit of the current nanosecond unixtime to its pool every time it 'ticks'. 33 // The more work the program does, the better the quality, as the internal schedular cannot immediately run the goroutine when it's ready. 34 func tickFeeder(ctx context.Context) error { 35 var value int64 36 var pushes int 37 feeder := NewFeeder() 38 defer feeder.CloseFeeder() 39 40 tickDuration := getTickFeederTickDuration() 41 42 for { 43 // wait for tick 44 time.Sleep(tickDuration) 45 46 // add tick value 47 value = (value << 1) | (time.Now().UnixNano() % 2) 48 pushes++ 49 50 if pushes >= 64 { 51 // convert to []byte 52 b := make([]byte, 8) 53 binary.LittleEndian.PutUint64(b, uint64(value)) 54 // reset 55 pushes = 0 56 57 // feed 58 select { 59 case feeder.input <- &entropyData{ 60 data: b, 61 entropy: 8, 62 }: 63 case <-ctx.Done(): 64 return nil 65 } 66 } else { 67 // check if are done 68 select { 69 case <-ctx.Done(): 70 return nil 71 default: 72 } 73 } 74 } 75 }