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