oklog / ulid

Universally Unique Lexicographically Sortable Identifier (ULID) in Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Switch defaultEntropy to math/rand/v2

bojanz opened this issue · comments

defaultEntropy() uses math/rand in order to be fast by default:

	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
BenchmarkNew/WithCryptoEntropy-8      2000000        771 ns/op      20.73 MB/s   16 B/op   1 allocs/op
BenchmarkNew/WithEntropy-8            20000000      65.8 ns/op     243.01 MB/s   16 B/op   1 allocs/op

Go 1.22 introduces math/rand/v2 which uses the ChaCha8Rand generator, greatly improving security with a minimal performance cost. From today's blog post:

For example, consider generating a random UUID. Since UUIDs are not secret, using math/rand might seem fine. But if math/rand has been seeded with the current time, then running it at the same instant on different computers will produce the same value, making them not “universally unique”. This is especially likely on systems where the current time is only available with millisecond precision. Even with auto-seeding using OS-provided entropy, as introduced in Go 1.20, the Go 1 generator’s seed is only a 63-bit integer, so a program that generates a UUID at startup can only generate 2⁶³ possible UUIDs and is likely to see collisions after 2³¹ or so UUIDs. Using Go 1.22, the new ChaCha8Rand generator is seeded from 256 bits of entropy and can generate 2²⁵⁶ possible first UUIDs. It does not need to worry about collisions.

Overall, ChaCha8Rand is slower than the Go 1 generator, but it is never more than twice as slow, and on typical servers the difference is never more than 3ns. Very few programs will be bottlenecked by this difference, and many programs will enjoy the improved security.

So, requiring Go 1.22 and switching defaultEntropy to math/rand/v2 sounds like a win.

Looks like this is not a simple drop-in replacement because the new Rand does not have a Read() method (yet):
golang/go#65562
golang/go#67059

Is the benefit purely or mostly derived from the additional entropy of the initial seed?

Twice as slow, even at the level of single-digit nanoseconds, is actually potentially meaningful for this package. I'll play around with the new package, let's see.

Looks like this is not a simple drop-in replacement because the new Rand does not have a Read() method (yet): golang/go#65562 golang/go#67059

It's intended. math/rand.Read has been deprecated: https://pkg.go.dev/math/rand@master#Read

It should use crypto/rand when a non-deterministic source is enough, or an algorithm from math/rand/v2

FWIW golang/go#67059 got accepted and merged, so rand.ChaCha8 will be a drop-in replacement from Go 1.23.

I have been looking into google/uuid v7 implementation. By default, crypto/rand.Read is used in NewRandom unless NewRandomFromReader is explicitly preferred.

Probably, that should be the default behavior which is also inline with the aforementioned deprecation.