Use crypto/rand for random id generation. (#1044)

* Use crypto/rand for random id generation.

Fixes #1043 and #1037

* Panic on rare crypto/rand error.

* Fixes in response to review.
This commit is contained in:
Jacob Hoffman-Andrews 2019-12-10 23:31:09 -08:00 committed by Miek Gieben
parent 6d0449f981
commit 8ebf2e419d
1 changed files with 10 additions and 42 deletions

52
msg.go
View File

@ -11,14 +11,12 @@ package dns
//go:generate go run msg_generate.go //go:generate go run msg_generate.go
import ( import (
crand "crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math/big" "math/big"
"math/rand"
"strconv" "strconv"
"strings" "strings"
"sync"
) )
const ( const (
@ -73,53 +71,23 @@ var (
ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication.
) )
// Id by default, returns a 16 bits random number to be used as a // Id by default returns a 16-bit random number to be used as a message id. The
// message id. The random provided should be good enough. This being a // number is drawn from a cryptographically secure random number generator.
// variable the function can be reassigned to a custom function. // This being a variable the function can be reassigned to a custom function.
// For instance, to make it return a static value: // For instance, to make it return a static value for testing:
// //
// dns.Id = func() uint16 { return 3 } // dns.Id = func() uint16 { return 3 }
var Id = id var Id = id
var (
idLock sync.Mutex
idRand *rand.Rand
)
// id returns a 16 bits random number to be used as a // id returns a 16 bits random number to be used as a
// message id. The random provided should be good enough. // message id. The random provided should be good enough.
func id() uint16 { func id() uint16 {
idLock.Lock() var output uint16
err := binary.Read(rand.Reader, binary.BigEndian, &output)
if idRand == nil { if err != nil {
// This (partially) works around panic("dns: reading random id failed: " + err.Error())
// https://github.com/golang/go/issues/11833 by only
// seeding idRand upon the first call to id.
var seed int64
var buf [8]byte
if _, err := crand.Read(buf[:]); err == nil {
seed = int64(binary.LittleEndian.Uint64(buf[:]))
} else {
seed = rand.Int63()
}
idRand = rand.New(rand.NewSource(seed))
} }
return output
// The call to idRand.Uint32 must be within the
// mutex lock because *rand.Rand is not safe for
// concurrent use.
//
// There is no added performance overhead to calling
// idRand.Uint32 inside a mutex lock over just
// calling rand.Uint32 as the global math/rand rng
// is internally protected by a sync.Mutex.
id := uint16(idRand.Uint32())
idLock.Unlock()
return id
} }
// MsgHdr is a a manually-unpacked version of (id, bits). // MsgHdr is a a manually-unpacked version of (id, bits).