github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/uuid/generator.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru> 12 // Use of this source code is governed by a MIT-style 13 // license that can be found in licenses/MIT-gofrs.txt. 14 15 // This code originated in github.com/gofrs/uuid. 16 17 package uuid 18 19 import ( 20 "crypto/md5" 21 "crypto/rand" 22 "crypto/sha1" 23 "encoding/binary" 24 "fmt" 25 "hash" 26 "io" 27 "net" 28 "sync" 29 "time" 30 31 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 32 "github.com/cockroachdb/errors" 33 ) 34 35 // Difference in 100-nanosecond intervals between 36 // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). 37 const epochStart = 122192928000000000 38 39 type epochFunc func() time.Time 40 41 // HWAddrFunc is the function type used to provide hardware (MAC) addresses. 42 type HWAddrFunc func() (net.HardwareAddr, error) 43 44 // DefaultGenerator is the default UUID Generator used by this package. 45 var DefaultGenerator Generator = NewGen() 46 47 // NewV1 returns a UUID based on the current timestamp and MAC address. 48 func NewV1() (UUID, error) { 49 return DefaultGenerator.NewV1() 50 } 51 52 // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. 53 func NewV3(ns UUID, name string) UUID { 54 return DefaultGenerator.NewV3(ns, name) 55 } 56 57 // NewV4 returns a randomly generated UUID. 58 func NewV4() (UUID, error) { 59 return DefaultGenerator.NewV4() 60 } 61 62 // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. 63 func NewV5(ns UUID, name string) UUID { 64 return DefaultGenerator.NewV5(ns, name) 65 } 66 67 // Generator provides an interface for generating UUIDs. 68 type Generator interface { 69 NewV1() (UUID, error) 70 // NewV2(domain byte) (UUID, error) // CRL: Removed support for V2. 71 NewV3(ns UUID, name string) UUID 72 NewV4() (UUID, error) 73 NewV5(ns UUID, name string) UUID 74 } 75 76 // Gen is a reference UUID generator based on the specifications laid out in 77 // RFC-4122 and DCE 1.1: Authentication and Security Services. This type 78 // satisfies the Generator interface as defined in this package. 79 // 80 // For consumers who are generating V1 UUIDs, but don't want to expose the MAC 81 // address of the node generating the UUIDs, the NewGenWithHWAF() function has been 82 // provided as a convenience. See the function's documentation for more info. 83 // 84 // The authors of this package do not feel that the majority of users will need 85 // to obfuscate their MAC address, and so we recommend using NewGen() to create 86 // a new generator. 87 type Gen struct { 88 clockSequenceOnce sync.Once 89 hardwareAddrOnce sync.Once 90 storageMutex syncutil.Mutex 91 92 rand io.Reader 93 94 epochFunc epochFunc 95 hwAddrFunc HWAddrFunc 96 lastTime uint64 97 clockSequence uint16 98 hardwareAddr [6]byte 99 } 100 101 // interface check -- build will fail if *Gen doesn't satisfy Generator 102 var _ Generator = (*Gen)(nil) 103 104 // NewGen returns a new instance of Gen with some default values set. Most 105 // people should use this. 106 // NewGen by default uses crypto/rand.Reader as its source of randomness. 107 func NewGen() *Gen { 108 return NewGenWithHWAF(defaultHWAddrFunc) 109 } 110 111 // NewGenWithReader returns a new instance of gen which uses r as its source of 112 // randomness. 113 func NewGenWithReader(r io.Reader) *Gen { 114 g := NewGen() 115 g.rand = r 116 return g 117 } 118 119 // NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most 120 // consumers should use NewGen() instead. 121 // 122 // This is used so that consumers can generate their own MAC addresses, for use 123 // in the generated UUIDs, if there is some concern about exposing the physical 124 // address of the machine generating the UUID. 125 // 126 // The Gen generator will only invoke the HWAddrFunc once, and cache that MAC 127 // address for all the future UUIDs generated by it. If you'd like to switch the 128 // MAC address being used, you'll need to create a new generator using this 129 // function. 130 func NewGenWithHWAF(hwaf HWAddrFunc) *Gen { 131 return &Gen{ 132 epochFunc: time.Now, 133 hwAddrFunc: hwaf, 134 rand: rand.Reader, 135 } 136 } 137 138 // NewV1 returns a UUID based on the current timestamp and MAC address. 139 func (g *Gen) NewV1() (UUID, error) { 140 u := UUID{} 141 142 timeNow, clockSeq, err := g.getClockSequence() 143 if err != nil { 144 return Nil, err 145 } 146 binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) 147 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) 148 binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) 149 binary.BigEndian.PutUint16(u[8:], clockSeq) 150 151 hardwareAddr, err := g.getHardwareAddr() 152 if err != nil { 153 return Nil, err 154 } 155 copy(u[10:], hardwareAddr) 156 157 u.SetVersion(V1) 158 u.SetVariant(VariantRFC4122) 159 160 return u, nil 161 } 162 163 // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. 164 func (g *Gen) NewV3(ns UUID, name string) UUID { 165 u := newFromHash(md5.New(), ns, name) 166 u.SetVersion(V3) 167 u.SetVariant(VariantRFC4122) 168 169 return u 170 } 171 172 // NewV4 returns a randomly generated UUID. 173 func (g *Gen) NewV4() (UUID, error) { 174 u := UUID{} 175 if r, ok := g.rand.(defaultRandReader); ok { 176 if n, err := r.Read(u[:]); n != len(u) { 177 panic("math/rand.Read always returns len(p)") 178 } else if err != nil { 179 panic("math/rand.Read always returns a nil error") 180 } 181 } else { 182 willEscape := UUID{} 183 if _, err := io.ReadFull(g.rand, willEscape[:]); err != nil { 184 return Nil, err 185 } 186 u = willEscape 187 } 188 u.SetVersion(V4) 189 u.SetVariant(VariantRFC4122) 190 191 return u, nil 192 } 193 194 // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. 195 func (g *Gen) NewV5(ns UUID, name string) UUID { 196 u := newFromHash(sha1.New(), ns, name) 197 u.SetVersion(V5) 198 u.SetVariant(VariantRFC4122) 199 200 return u 201 } 202 203 // Returns the epoch and clock sequence. 204 func (g *Gen) getClockSequence() (uint64, uint16, error) { 205 var err error 206 g.clockSequenceOnce.Do(func() { 207 buf := make([]byte, 2) 208 if _, err = io.ReadFull(g.rand, buf); err != nil { 209 return 210 } 211 g.clockSequence = binary.BigEndian.Uint16(buf) 212 }) 213 if err != nil { 214 return 0, 0, err 215 } 216 217 g.storageMutex.Lock() 218 defer g.storageMutex.Unlock() 219 220 timeNow := g.getEpoch() 221 // Clock didn't change since last UUID generation. 222 // Should increase clock sequence. 223 if timeNow <= g.lastTime { 224 g.clockSequence++ 225 } 226 g.lastTime = timeNow 227 228 return timeNow, g.clockSequence, nil 229 } 230 231 // Returns the hardware address. 232 func (g *Gen) getHardwareAddr() ([]byte, error) { 233 var err error 234 g.hardwareAddrOnce.Do(func() { 235 var hwAddr net.HardwareAddr 236 if hwAddr, err = g.hwAddrFunc(); err == nil { 237 copy(g.hardwareAddr[:], hwAddr) 238 return 239 } 240 241 // Initialize hardwareAddr randomly in case 242 // of real network interfaces absence. 243 if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil { 244 return 245 } 246 // Set multicast bit as recommended by RFC-4122 247 g.hardwareAddr[0] |= 0x01 248 }) 249 if err != nil { 250 return []byte{}, err 251 } 252 return g.hardwareAddr[:], nil 253 } 254 255 // Returns the difference between UUID epoch (October 15, 1582) 256 // and current time in 100-nanosecond intervals. 257 func (g *Gen) getEpoch() uint64 { 258 return epochStart + uint64(g.epochFunc().UnixNano()/100) 259 } 260 261 // Returns the UUID based on the hashing of the namespace UUID and name. 262 func newFromHash(h hash.Hash, ns UUID, name string) UUID { 263 u := UUID{} 264 mustWrite := func(data []byte) { 265 if _, err := h.Write(data); err != nil { 266 panic(errors.Wrap(err, "failed to write to hash")) 267 } 268 } 269 mustWrite(ns[:]) 270 mustWrite([]byte(name)) 271 copy(u[:], h.Sum(nil)) 272 return u 273 } 274 275 // Returns the hardware address. 276 func defaultHWAddrFunc() (net.HardwareAddr, error) { 277 ifaces, err := net.Interfaces() 278 if err != nil { 279 return []byte{}, err 280 } 281 for _, iface := range ifaces { 282 if len(iface.HardwareAddr) >= 6 { 283 return iface.HardwareAddr, nil 284 } 285 } 286 return []byte{}, fmt.Errorf("uuid: no HW address found") 287 }