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] }