generator.go raw

   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