1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- package identity
- import (
- cryptorand "crypto/rand"
- "fmt"
- "io"
- "math/big"
- )
- var (
- // idReader is used for random id generation. This declaration allows us to
- // replace it for testing.
- idReader = cryptorand.Reader
- )
- // parameters for random identifier generation. We can tweak this when there is
- // time for further analysis.
- const (
- randomIDEntropyBytes = 17
- randomIDBase = 36
- // To ensure that all identifiers are fixed length, we make sure they
- // get padded out or truncated to 25 characters.
- //
- // For academics, f5lxx1zz5pnorynqglhzmsp33 == 2^128 - 1. This value
- // was calculated from floor(log(2^128-1, 36)) + 1.
- //
- // While 128 bits is the largest whole-byte size that fits into 25
- // base-36 characters, we generate an extra byte of entropy to fill
- // in the high bits, which would otherwise be 0. This gives us a more
- // even distribution of the first character.
- //
- // See http://mathworld.wolfram.com/NumberLength.html for more information.
- maxRandomIDLength = 25
- )
- // NewID generates a new identifier for use where random identifiers with low
- // collision probability are required.
- //
- // With the parameters in this package, the generated identifier will provide
- // ~129 bits of entropy encoded with base36. Leading padding is added if the
- // string is less 25 bytes. We do not intend to maintain this interface, so
- // identifiers should be treated opaquely.
- func NewID() string {
- var p [randomIDEntropyBytes]byte
- if _, err := io.ReadFull(idReader, p[:]); err != nil {
- panic(fmt.Errorf("failed to read random bytes: %v", err))
- }
- p[0] |= 0x80 // set high bit to avoid the need for padding
- return (&big.Int{}).SetBytes(p[:]).Text(randomIDBase)[1 : maxRandomIDLength+1]
- }
|