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 22package uuid 23 24import ( 25 "crypto/md5" 26 "crypto/rand" 27 "crypto/sha1" 28 "encoding/binary" 29 "hash" 30 "net" 31 "os" 32 "sync" 33 "time" 34) 35 36// Difference in 100-nanosecond intervals between 37// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). 38const epochStart = 122192928000000000 39 40var ( 41 global = newDefaultGenerator() 42 43 epochFunc = unixTimeFunc 44 posixUID = uint32(os.Getuid()) 45 posixGID = uint32(os.Getgid()) 46) 47 48// NewV1 returns UUID based on current timestamp and MAC address. 49func NewV1() UUID { 50 return global.NewV1() 51} 52 53// NewV2 returns DCE Security UUID based on POSIX UID/GID. 54func NewV2(domain byte) UUID { 55 return global.NewV2(domain) 56} 57 58// NewV3 returns UUID based on MD5 hash of namespace UUID and name. 59func NewV3(ns UUID, name string) UUID { 60 return global.NewV3(ns, name) 61} 62 63// NewV4 returns random generated UUID. 64func NewV4() UUID { 65 return global.NewV4() 66} 67 68// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. 69func NewV5(ns UUID, name string) UUID { 70 return global.NewV5(ns, name) 71} 72 73// Generator provides interface for generating UUIDs. 74type Generator interface { 75 NewV1() UUID 76 NewV2(domain byte) UUID 77 NewV3(ns UUID, name string) UUID 78 NewV4() UUID 79 NewV5(ns UUID, name string) UUID 80} 81 82// Default generator implementation. 83type generator struct { 84 storageOnce sync.Once 85 storageMutex sync.Mutex 86 87 lastTime uint64 88 clockSequence uint16 89 hardwareAddr [6]byte 90} 91 92func newDefaultGenerator() Generator { 93 return &generator{} 94} 95 96// NewV1 returns UUID based on current timestamp and MAC address. 97func (g *generator) NewV1() UUID { 98 u := UUID{} 99 100 timeNow, clockSeq, hardwareAddr := g.getStorage() 101 102 binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) 103 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) 104 binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) 105 binary.BigEndian.PutUint16(u[8:], clockSeq) 106 107 copy(u[10:], hardwareAddr) 108 109 u.SetVersion(V1) 110 u.SetVariant(VariantRFC4122) 111 112 return u 113} 114 115// NewV2 returns DCE Security UUID based on POSIX UID/GID. 116func (g *generator) NewV2(domain byte) UUID { 117 u := UUID{} 118 119 timeNow, clockSeq, hardwareAddr := g.getStorage() 120 121 switch domain { 122 case DomainPerson: 123 binary.BigEndian.PutUint32(u[0:], posixUID) 124 case DomainGroup: 125 binary.BigEndian.PutUint32(u[0:], posixGID) 126 } 127 128 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) 129 binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) 130 binary.BigEndian.PutUint16(u[8:], clockSeq) 131 u[9] = domain 132 133 copy(u[10:], hardwareAddr) 134 135 u.SetVersion(V2) 136 u.SetVariant(VariantRFC4122) 137 138 return u 139} 140 141// NewV3 returns UUID based on MD5 hash of namespace UUID and name. 142func (g *generator) NewV3(ns UUID, name string) UUID { 143 u := newFromHash(md5.New(), ns, name) 144 u.SetVersion(V3) 145 u.SetVariant(VariantRFC4122) 146 147 return u 148} 149 150// NewV4 returns random generated UUID. 151func (g *generator) NewV4() UUID { 152 u := UUID{} 153 g.safeRandom(u[:]) 154 u.SetVersion(V4) 155 u.SetVariant(VariantRFC4122) 156 157 return u 158} 159 160// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. 161func (g *generator) NewV5(ns UUID, name string) UUID { 162 u := newFromHash(sha1.New(), ns, name) 163 u.SetVersion(V5) 164 u.SetVariant(VariantRFC4122) 165 166 return u 167} 168 169func (g *generator) initStorage() { 170 g.initClockSequence() 171 g.initHardwareAddr() 172} 173 174func (g *generator) initClockSequence() { 175 buf := make([]byte, 2) 176 g.safeRandom(buf) 177 g.clockSequence = binary.BigEndian.Uint16(buf) 178} 179 180func (g *generator) initHardwareAddr() { 181 interfaces, err := net.Interfaces() 182 if err == nil { 183 for _, iface := range interfaces { 184 if len(iface.HardwareAddr) >= 6 { 185 copy(g.hardwareAddr[:], iface.HardwareAddr) 186 return 187 } 188 } 189 } 190 191 // Initialize hardwareAddr randomly in case 192 // of real network interfaces absence 193 g.safeRandom(g.hardwareAddr[:]) 194 195 // Set multicast bit as recommended in RFC 4122 196 g.hardwareAddr[0] |= 0x01 197} 198 199func (g *generator) safeRandom(dest []byte) { 200 if _, err := rand.Read(dest); err != nil { 201 panic(err) 202 } 203} 204 205// Returns UUID v1/v2 storage state. 206// Returns epoch timestamp, clock sequence, and hardware address. 207func (g *generator) getStorage() (uint64, uint16, []byte) { 208 g.storageOnce.Do(g.initStorage) 209 210 g.storageMutex.Lock() 211 defer g.storageMutex.Unlock() 212 213 timeNow := epochFunc() 214 // Clock changed backwards since last UUID generation. 215 // Should increase clock sequence. 216 if timeNow <= g.lastTime { 217 g.clockSequence++ 218 } 219 g.lastTime = timeNow 220 221 return timeNow, g.clockSequence, g.hardwareAddr[:] 222} 223 224// Returns difference in 100-nanosecond intervals between 225// UUID epoch (October 15, 1582) and current time. 226// This is default epoch calculation function. 227func unixTimeFunc() uint64 { 228 return epochStart + uint64(time.Now().UnixNano()/100) 229} 230 231// Returns UUID based on hashing of namespace UUID and name. 232func newFromHash(h hash.Hash, ns UUID, name string) UUID { 233 u := UUID{} 234 h.Write(ns[:]) 235 h.Write([]byte(name)) 236 copy(u[:], h.Sum(nil)) 237 238 return u 239} 240