1 // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 22 package uuid
23 24 import (
25 "crypto/md5"
26 "crypto/rand"
27 "crypto/sha1"
28 "encoding/binary"
29 "fmt"
30 "hash"
31 "io"
32 "net"
33 "sync"
34 "time"
35 )
36 37 // Difference in 100-nanosecond intervals between
38 // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
39 const epochStart = 122192928000000000
40 41 // EpochFunc is the function type used to provide the current time.
42 type EpochFunc func() time.Time
43 44 // HWAddrFunc is the function type used to provide hardware (MAC) addresses.
45 type HWAddrFunc func() (net.HardwareAddr, error)
46 47 // DefaultGenerator is the default UUID Generator used by this package.
48 var DefaultGenerator Generator = NewGen()
49 50 // NewV1 returns a UUID based on the current timestamp and MAC address.
51 func NewV1() (UUID, error) {
52 return DefaultGenerator.NewV1()
53 }
54 55 // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
56 func NewV3(ns UUID, name string) UUID {
57 return DefaultGenerator.NewV3(ns, name)
58 }
59 60 // NewV4 returns a randomly generated UUID.
61 func NewV4() (UUID, error) {
62 return DefaultGenerator.NewV4()
63 }
64 65 // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.
66 func NewV5(ns UUID, name string) UUID {
67 return DefaultGenerator.NewV5(ns, name)
68 }
69 70 // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
71 // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
72 // order being adjusted to allow the UUID to be k-sortable.
73 //
74 // This is implemented based on revision 03 of the Peabody UUID draft, and may
75 // be subject to change pending further revisions. Until the final specification
76 // revision is finished, changes required to implement updates to the spec will
77 // not be considered a breaking change. They will happen as a minor version
78 // releases until the spec is final.
79 func NewV6() (UUID, error) {
80 return DefaultGenerator.NewV6()
81 }
82 83 // NewV7 returns a k-sortable UUID based on the current millisecond precision
84 // UNIX epoch and 74 bits of pseudorandom data. It supports single-node batch generation (multiple UUIDs in the same timestamp) with a Monotonic Random counter.
85 //
86 // This is implemented based on revision 04 of the Peabody UUID draft, and may
87 // be subject to change pending further revisions. Until the final specification
88 // revision is finished, changes required to implement updates to the spec will
89 // not be considered a breaking change. They will happen as a minor version
90 // releases until the spec is final.
91 func NewV7() (UUID, error) {
92 return DefaultGenerator.NewV7()
93 }
94 95 // Generator provides an interface for generating UUIDs.
96 type Generator interface {
97 NewV1() (UUID, error)
98 NewV3(ns UUID, name string) UUID
99 NewV4() (UUID, error)
100 NewV5(ns UUID, name string) UUID
101 NewV6() (UUID, error)
102 NewV7() (UUID, error)
103 }
104 105 // Gen is a reference UUID generator based on the specifications laid out in
106 // RFC-4122 and DCE 1.1: Authentication and Security Services. This type
107 // satisfies the Generator interface as defined in this package.
108 //
109 // For consumers who are generating V1 UUIDs, but don't want to expose the MAC
110 // address of the node generating the UUIDs, the NewGenWithHWAF() function has been
111 // provided as a convenience. See the function's documentation for more info.
112 //
113 // The authors of this package do not feel that the majority of users will need
114 // to obfuscate their MAC address, and so we recommend using NewGen() to create
115 // a new generator.
116 type Gen struct {
117 clockSequenceOnce sync.Once
118 hardwareAddrOnce sync.Once
119 storageMutex sync.Mutex
120 121 rand io.Reader
122 123 epochFunc EpochFunc
124 hwAddrFunc HWAddrFunc
125 lastTime uint64
126 clockSequence uint16
127 hardwareAddr [6]byte
128 }
129 130 // GenOption is a function type that can be used to configure a Gen generator.
131 type GenOption func(*Gen)
132 133 // interface check -- build will fail if *Gen doesn't satisfy Generator
134 var _ Generator = (*Gen)(nil)
135 136 // NewGen returns a new instance of Gen with some default values set. Most
137 // people should use this.
138 func NewGen() *Gen {
139 return NewGenWithHWAF(defaultHWAddrFunc)
140 }
141 142 // NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most
143 // consumers should use NewGen() instead.
144 //
145 // This is used so that consumers can generate their own MAC addresses, for use
146 // in the generated UUIDs, if there is some concern about exposing the physical
147 // address of the machine generating the UUID.
148 //
149 // The Gen generator will only invoke the HWAddrFunc once, and cache that MAC
150 // address for all the future UUIDs generated by it. If you'd like to switch the
151 // MAC address being used, you'll need to create a new generator using this
152 // function.
153 func NewGenWithHWAF(hwaf HWAddrFunc) *Gen {
154 return NewGenWithOptions(WithHWAddrFunc(hwaf))
155 }
156 157 // NewGenWithOptions returns a new instance of Gen with the options provided.
158 // Most people should use NewGen() or NewGenWithHWAF() instead.
159 //
160 // To customize the generator, you can pass in one or more GenOption functions.
161 // For example:
162 //
163 // gen := NewGenWithOptions(
164 // WithHWAddrFunc(myHWAddrFunc),
165 // WithEpochFunc(myEpochFunc),
166 // WithRandomReader(myRandomReader),
167 // )
168 //
169 // NewGenWithOptions(WithHWAddrFunc(myHWAddrFunc)) is equivalent to calling
170 // NewGenWithHWAF(myHWAddrFunc)
171 // NewGenWithOptions() is equivalent to calling NewGen()
172 func NewGenWithOptions(opts ...GenOption) *Gen {
173 gen := &Gen{
174 epochFunc: time.Now,
175 hwAddrFunc: defaultHWAddrFunc,
176 rand: rand.Reader,
177 }
178 179 for _, opt := range opts {
180 opt(gen)
181 }
182 183 return gen
184 }
185 186 // WithHWAddrFunc is a GenOption that allows you to provide your own HWAddrFunc
187 // function.
188 // When this option is nil, the defaultHWAddrFunc is used.
189 func WithHWAddrFunc(hwaf HWAddrFunc) GenOption {
190 return func(gen *Gen) {
191 if hwaf == nil {
192 hwaf = defaultHWAddrFunc
193 }
194 195 gen.hwAddrFunc = hwaf
196 }
197 }
198 199 // WithEpochFunc is a GenOption that allows you to provide your own EpochFunc
200 // function.
201 // When this option is nil, time.Now is used.
202 func WithEpochFunc(epochf EpochFunc) GenOption {
203 return func(gen *Gen) {
204 if epochf == nil {
205 epochf = time.Now
206 }
207 208 gen.epochFunc = epochf
209 }
210 }
211 212 // WithRandomReader is a GenOption that allows you to provide your own random
213 // reader.
214 // When this option is nil, the default rand.Reader is used.
215 func WithRandomReader(reader io.Reader) GenOption {
216 return func(gen *Gen) {
217 if reader == nil {
218 reader = rand.Reader
219 }
220 221 gen.rand = reader
222 }
223 }
224 225 // NewV1 returns a UUID based on the current timestamp and MAC address.
226 func (g *Gen) NewV1() (UUID, error) {
227 u := UUID{}
228 229 timeNow, clockSeq, err := g.getClockSequence(false)
230 if err != nil {
231 return Nil, err
232 }
233 binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
234 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
235 binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
236 binary.BigEndian.PutUint16(u[8:], clockSeq)
237 238 hardwareAddr, err := g.getHardwareAddr()
239 if err != nil {
240 return Nil, err
241 }
242 copy(u[10:], hardwareAddr)
243 244 u.SetVersion(V1)
245 u.SetVariant(VariantRFC4122)
246 247 return u, nil
248 }
249 250 // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
251 func (g *Gen) NewV3(ns UUID, name string) UUID {
252 u := newFromHash(md5.New(), ns, name)
253 u.SetVersion(V3)
254 u.SetVariant(VariantRFC4122)
255 256 return u
257 }
258 259 // NewV4 returns a randomly generated UUID.
260 func (g *Gen) NewV4() (UUID, error) {
261 u := UUID{}
262 if _, err := io.ReadFull(g.rand, u[:]); err != nil {
263 return Nil, err
264 }
265 u.SetVersion(V4)
266 u.SetVariant(VariantRFC4122)
267 268 return u, nil
269 }
270 271 // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.
272 func (g *Gen) NewV5(ns UUID, name string) UUID {
273 u := newFromHash(sha1.New(), ns, name)
274 u.SetVersion(V5)
275 u.SetVariant(VariantRFC4122)
276 277 return u
278 }
279 280 // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
281 // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
282 // order being adjusted to allow the UUID to be k-sortable.
283 //
284 // This is implemented based on revision 03 of the Peabody UUID draft, and may
285 // be subject to change pending further revisions. Until the final specification
286 // revision is finished, changes required to implement updates to the spec will
287 // not be considered a breaking change. They will happen as a minor version
288 // releases until the spec is final.
289 func (g *Gen) NewV6() (UUID, error) {
290 var u UUID
291 292 if _, err := io.ReadFull(g.rand, u[10:]); err != nil {
293 return Nil, err
294 }
295 296 timeNow, clockSeq, err := g.getClockSequence(false)
297 if err != nil {
298 return Nil, err
299 }
300 301 binary.BigEndian.PutUint32(u[0:], uint32(timeNow>>28)) // set time_high
302 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>12)) // set time_mid
303 binary.BigEndian.PutUint16(u[6:], uint16(timeNow&0xfff)) // set time_low (minus four version bits)
304 binary.BigEndian.PutUint16(u[8:], clockSeq&0x3fff) // set clk_seq_hi_res (minus two variant bits)
305 306 u.SetVersion(V6)
307 u.SetVariant(VariantRFC4122)
308 309 return u, nil
310 }
311 312 // getClockSequence returns the epoch and clock sequence for V1,V6 and V7 UUIDs.
313 //
314 // When useUnixTSMs is false, it uses the Coordinated Universal Time (UTC) as a count of 100-
315 //
316 // nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar).
317 func (g *Gen) getClockSequence(useUnixTSMs bool) (uint64, uint16, error) {
318 var err error
319 g.clockSequenceOnce.Do(func() {
320 buf := make([]byte, 2)
321 if _, err = io.ReadFull(g.rand, buf); err != nil {
322 return
323 }
324 g.clockSequence = binary.BigEndian.Uint16(buf)
325 })
326 if err != nil {
327 return 0, 0, err
328 }
329 330 g.storageMutex.Lock()
331 defer g.storageMutex.Unlock()
332 333 var timeNow uint64
334 if useUnixTSMs {
335 timeNow = uint64(g.epochFunc().UnixMilli())
336 } else {
337 timeNow = g.getEpoch()
338 }
339 // Clock didn't change since last UUID generation.
340 // Should increase clock sequence.
341 if timeNow <= g.lastTime {
342 g.clockSequence++
343 }
344 g.lastTime = timeNow
345 346 return timeNow, g.clockSequence, nil
347 }
348 349 // NewV7 returns a k-sortable UUID based on the current millisecond precision
350 // UNIX epoch and 74 bits of pseudorandom data.
351 //
352 // This is implemented based on revision 04 of the Peabody UUID draft, and may
353 // be subject to change pending further revisions. Until the final specification
354 // revision is finished, changes required to implement updates to the spec will
355 // not be considered a breaking change. They will happen as a minor version
356 // releases until the spec is final.
357 func (g *Gen) NewV7() (UUID, error) {
358 var u UUID
359 /* https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-7
360 0 1 2 3
361 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
362 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363 | unix_ts_ms |
364 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 | unix_ts_ms | ver | rand_a |
366 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 |var| rand_b |
368 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 | rand_b |
370 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
371 372 ms, clockSeq, err := g.getClockSequence(true)
373 if err != nil {
374 return Nil, err
375 }
376 //UUIDv7 features a 48 bit timestamp. First 32bit (4bytes) represents seconds since 1970, followed by 2 bytes for the ms granularity.
377 u[0] = byte(ms >> 40) //1-6 bytes: big-endian unsigned number of Unix epoch timestamp
378 u[1] = byte(ms >> 32)
379 u[2] = byte(ms >> 24)
380 u[3] = byte(ms >> 16)
381 u[4] = byte(ms >> 8)
382 u[5] = byte(ms)
383 384 //support batching by using a monotonic pseudo-random sequence
385 //The 6th byte contains the version and partially rand_a data.
386 //We will lose the most significant bites from the clockSeq (with SetVersion), but it is ok, we need the least significant that contains the counter to ensure the monotonic property
387 binary.BigEndian.PutUint16(u[6:8], clockSeq) // set rand_a with clock seq which is random and monotonic
388 389 //override first 4bits of u[6].
390 u.SetVersion(V7)
391 392 //set rand_b 64bits of pseudo-random bits (first 2 will be overridden)
393 if _, err = io.ReadFull(g.rand, u[8:16]); err != nil {
394 return Nil, err
395 }
396 //override first 2 bits of byte[8] for the variant
397 u.SetVariant(VariantRFC4122)
398 399 return u, nil
400 }
401 402 // Returns the hardware address.
403 func (g *Gen) getHardwareAddr() ([]byte, error) {
404 var err error
405 g.hardwareAddrOnce.Do(func() {
406 var hwAddr net.HardwareAddr
407 if hwAddr, err = g.hwAddrFunc(); err == nil {
408 copy(g.hardwareAddr[:], hwAddr)
409 return
410 }
411 412 // Initialize hardwareAddr randomly in case
413 // of real network interfaces absence.
414 if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil {
415 return
416 }
417 // Set multicast bit as recommended by RFC-4122
418 g.hardwareAddr[0] |= 0x01
419 })
420 if err != nil {
421 return []byte{}, err
422 }
423 return g.hardwareAddr[:], nil
424 }
425 426 // Returns the difference between UUID epoch (October 15, 1582)
427 // and current time in 100-nanosecond intervals.
428 func (g *Gen) getEpoch() uint64 {
429 return epochStart + uint64(g.epochFunc().UnixNano()/100)
430 }
431 432 // Returns the UUID based on the hashing of the namespace UUID and name.
433 func newFromHash(h hash.Hash, ns UUID, name string) UUID {
434 u := UUID{}
435 h.Write(ns[:])
436 h.Write([]byte(name))
437 copy(u[:], h.Sum(nil))
438 439 return u
440 }
441 442 var netInterfaces = net.Interfaces
443 444 // Returns the hardware address.
445 func defaultHWAddrFunc() (net.HardwareAddr, error) {
446 ifaces, err := netInterfaces()
447 if err != nil {
448 return []byte{}, err
449 }
450 for _, iface := range ifaces {
451 if len(iface.HardwareAddr) >= 6 {
452 return iface.HardwareAddr, nil
453 }
454 }
455 return []byte{}, fmt.Errorf("uuid: no HW address found")
456 }
457