Skip to content

Commit

Permalink
Add ID4() function to return entropy errors
Browse files Browse the repository at this point in the history
This new function can support the desired feature (return error
instead of panic) without having to break compatibility for existing
clients.

Fixes satori#66.
Fixes satori#67.
Fixes satori#68.
Fixes satori#69.
Fixes satori#70.
  • Loading branch information
kevinburke committed Jan 25, 2018
1 parent 348e44b commit 24443c6
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 15 deletions.
47 changes: 32 additions & 15 deletions generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,23 @@ func NewV2(domain byte) UUID {
return global.NewV2(domain)
}

// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
// NewV3 returns a UUID based on the MD5 hash of namespace UUID and name.
func NewV3(ns UUID, name string) UUID {
return global.NewV3(ns, name)
}

// NewV4 returns random generated UUID.
// NewV4 returns a randomly generated UUID.
func NewV4() UUID {
u, err := ID4()
if err != nil {
panic(err)
}
return u
}

// ID4 returns a randomly generated UUID, or an error if there was not enough
// entropy.
func ID4() (UUID, error) {
return global.NewV4()
}

Expand Down Expand Up @@ -89,7 +99,7 @@ type generator struct {
hardwareAddr [6]byte
}

func newDefaultGenerator() Generator {
func newDefaultGenerator() *generator {
return &generator{}
}

Expand Down Expand Up @@ -148,13 +158,15 @@ func (g *generator) NewV3(ns UUID, name string) UUID {
}

// NewV4 returns random generated UUID.
func (g *generator) NewV4() UUID {
func (g *generator) NewV4() (UUID, error) {
u := UUID{}
g.safeRandom(u[:])
if err := g.random(u[:]); err != nil {
return u, err
}
u.SetVersion(V4)
u.SetVariant(VariantRFC4122)

return u
return u, nil
}

// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
Expand All @@ -171,35 +183,40 @@ func (g *generator) initStorage() {
g.initHardwareAddr()
}

func (g *generator) initClockSequence() {
func (g *generator) initClockSequence() error {
buf := make([]byte, 2)
g.safeRandom(buf)
if err := g.random(buf); err != nil {
return err
}
g.clockSequence = binary.BigEndian.Uint16(buf)
return nil
}

func (g *generator) initHardwareAddr() {
func (g *generator) initHardwareAddr() error {
interfaces, err := net.Interfaces()
if err == nil {
for _, iface := range interfaces {
if len(iface.HardwareAddr) >= 6 {
copy(g.hardwareAddr[:], iface.HardwareAddr)
return
return nil
}
}
}

// Initialize hardwareAddr randomly in case
// of real network interfaces absence
g.safeRandom(g.hardwareAddr[:])
if err := g.random(g.hardwareAddr[:]); err != nil {
return err
}

// Set multicast bit as recommended in RFC 4122
g.hardwareAddr[0] |= 0x01
return nil
}

func (g *generator) safeRandom(dest []byte) {
if _, err := rand.Read(dest); err != nil {
panic(err)
}
func (g *generator) random(dest []byte) error {
_, err := rand.Read(dest)
return err
}

// Returns UUID v1/v2 storage state.
Expand Down
7 changes: 7 additions & 0 deletions generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ func (s *genTestSuite) TestNewV4(c *C) {
c.Assert(u.Variant(), Equals, VariantRFC4122)
}

func (s *genTestSuite) TestID4(c *C) {
u, err := ID4()
c.Assert(err, IsNil)
c.Assert(u.Version(), Equals, V4)
c.Assert(u.Variant(), Equals, VariantRFC4122)
}

func (s *genTestSuite) BenchmarkNewV4(c *C) {
for i := 0; i < c.N; i++ {
NewV4()
Expand Down

0 comments on commit 24443c6

Please sign in to comment.