diff --git a/dmarcdb/eval.go b/dmarcdb/eval.go index 14fc3b0..b293b07 100644 --- a/dmarcdb/eval.go +++ b/dmarcdb/eval.go @@ -272,7 +272,7 @@ func RemoveEvaluationsDomain(ctx context.Context, domain dns.Domain) error { return err } -var jitterRand = mox.NewRand() +var jitterRand = mox.NewPseudoRand() // time to sleep until next whole hour t, replaced by tests. // Jitter so we don't cause load at exactly whole hours, other processes may diff --git a/mox-/msgid.go b/mox-/msgid.go index 9fe2f6b..9adae47 100644 --- a/mox-/msgid.go +++ b/mox-/msgid.go @@ -1,14 +1,13 @@ package mox import ( + cryptorand "crypto/rand" "encoding/base64" ) -var messageIDRand = NewRand() - // MessageIDGen returns a generated unique random Message-Id value, excluding <>. func MessageIDGen(smtputf8 bool) string { buf := make([]byte, 16) - messageIDRand.Read(buf) + cryptorand.Read(buf) return base64.RawURLEncoding.EncodeToString(buf) + "@" + Conf.Static.HostnameDomain.XName(smtputf8) } diff --git a/mox-/rand.go b/mox-/rand.go index 4fd3ee9..2881f68 100644 --- a/mox-/rand.go +++ b/mox-/rand.go @@ -5,11 +5,24 @@ import ( "encoding/binary" "fmt" mathrand "math/rand" + "sync" ) -// NewRand returns a new PRNG seeded with random bytes from crypto/rand. -func NewRand() *mathrand.Rand { - return mathrand.New(mathrand.NewSource(CryptoRandInt())) +type rand struct { + *mathrand.Rand + sync.Mutex +} + +// NewPseudoRand returns a new PRNG seeded with random bytes from crypto/rand. +func NewPseudoRand() *rand { + return &rand{Rand: mathrand.New(mathrand.NewSource(CryptoRandInt()))} +} + +// Read can be called concurrently. +func (r *rand) Read(buf []byte) (int, error) { + r.Lock() + defer r.Unlock() + return r.Rand.Read(buf) } // CryptoRandInt returns a cryptographically random number. diff --git a/queue/queue.go b/queue/queue.go index c63721c..f615cb0 100644 --- a/queue/queue.go +++ b/queue/queue.go @@ -61,7 +61,7 @@ var ( ) ) -var jitter = mox.NewRand() +var jitter = mox.NewPseudoRand() var DBTypes = []any{Msg{}} // Types stored in DB. var DB *bstore.DB // Exported for making backups. diff --git a/quickstart.go b/quickstart.go index 9b0ee39..6f1c797 100644 --- a/quickstart.go +++ b/quickstart.go @@ -40,11 +40,19 @@ import ( var moxService string func pwgen() string { - rand := mox.NewRand() chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*-_;:,<.>/" s := "" + buf := make([]byte, 1) for i := 0; i < 12; i++ { - s += string(chars[rand.Intn(len(chars))]) + for { + cryptorand.Read(buf) + i := int(buf[0]) + if i+len(chars) > 255 { + continue // Prevent bias. + } + s += string(chars[i%len(chars)]) + break + } } return s } diff --git a/smtpserver/server.go b/smtpserver/server.go index 1b382e6..f1e4e9a 100644 --- a/smtpserver/server.go +++ b/smtpserver/server.go @@ -168,7 +168,7 @@ var ( ) ) -var jitterRand = mox.NewRand() +var jitterRand = mox.NewPseudoRand() func durationDefault(delay *time.Duration, def time.Duration) time.Duration { if delay == nil { diff --git a/store/account.go b/store/account.go index 33a2bc1..69d72ed 100644 --- a/store/account.go +++ b/store/account.go @@ -24,6 +24,7 @@ package store import ( "context" "crypto/md5" + cryptorand "crypto/rand" "crypto/sha1" "crypto/sha256" "encoding" @@ -74,8 +75,6 @@ var ( ErrAccountUnknown = errors.New("no such account") ) -var subjectpassRand = mox.NewRand() - var DefaultInitialMailboxes = config.InitialMailboxes{ SpecialUse: config.SpecialUseMailboxes{ Sent: "Sent", @@ -1460,8 +1459,12 @@ func (a *Account) Subjectpass(email string) (key string, err error) { } key = "" const chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - for i := 0; i < 16; i++ { - key += string(chars[subjectpassRand.Intn(len(chars))]) + buf := make([]byte, 16) + if _, err := cryptorand.Read(buf); err != nil { + return err + } + for _, b := range buf { + key += string(chars[int(b)%len(chars)]) } v.Key = key return tx.Insert(&v)