1// This package provides immutable UUID structs and the functions
2// NewV3, NewV4, NewV5 and Parse() for generating versions 3, 4
3// and 5 UUIDs as specified in RFC 4122.
4//
5// Copyright (C) 2011 by Krzysztof Kowalik <chris@nu7hat.ch>
6package uuid
7
8import (
9	"crypto/md5"
10	"crypto/rand"
11	"crypto/sha1"
12	"encoding/hex"
13	"errors"
14	"fmt"
15	"hash"
16	"regexp"
17)
18
19// The UUID reserved variants.
20const (
21	ReservedNCS       byte = 0x80
22	ReservedRFC4122   byte = 0x40
23	ReservedMicrosoft byte = 0x20
24	ReservedFuture    byte = 0x00
25)
26
27// The following standard UUIDs are for use with NewV3() or NewV5().
28var (
29	NamespaceDNS, _  = ParseHex("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
30	NamespaceURL, _  = ParseHex("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
31	NamespaceOID, _  = ParseHex("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
32	NamespaceX500, _ = ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
33)
34
35// Pattern used to parse hex string representation of the UUID.
36// FIXME: do something to consider both brackets at one time,
37// current one allows to parse string with only one opening
38// or closing bracket.
39const hexPattern = "^(urn\\:uuid\\:)?\\{?([a-z0-9]{8})-([a-z0-9]{4})-" +
40	"([1-5][a-z0-9]{3})-([a-z0-9]{4})-([a-z0-9]{12})\\}?$"
41
42var re = regexp.MustCompile(hexPattern)
43
44// A UUID representation compliant with specification in
45// RFC 4122 document.
46type UUID [16]byte
47
48// ParseHex creates a UUID object from given hex string
49// representation. Function accepts UUID string in following
50// formats:
51//
52//     uuid.ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
53//     uuid.ParseHex("{6ba7b814-9dad-11d1-80b4-00c04fd430c8}")
54//     uuid.ParseHex("urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8")
55//
56func ParseHex(s string) (u *UUID, err error) {
57	md := re.FindStringSubmatch(s)
58	if md == nil {
59		err = errors.New("Invalid UUID string")
60		return
61	}
62	hash := md[2] + md[3] + md[4] + md[5] + md[6]
63	b, err := hex.DecodeString(hash)
64	if err != nil {
65		return
66	}
67	u = new(UUID)
68	copy(u[:], b)
69	return
70}
71
72// Parse creates a UUID object from given bytes slice.
73func Parse(b []byte) (u *UUID, err error) {
74	if len(b) != 16 {
75		err = errors.New("Given slice is not valid UUID sequence")
76		return
77	}
78	u = new(UUID)
79	copy(u[:], b)
80	return
81}
82
83// Generate a UUID based on the MD5 hash of a namespace identifier
84// and a name.
85func NewV3(ns *UUID, name []byte) (u *UUID, err error) {
86	if ns == nil {
87		err = errors.New("Invalid namespace UUID")
88		return
89	}
90	u = new(UUID)
91	// Set all bits to MD5 hash generated from namespace and name.
92	u.setBytesFromHash(md5.New(), ns[:], name)
93	u.setVariant(ReservedRFC4122)
94	u.setVersion(3)
95	return
96}
97
98// Generate a random UUID.
99func NewV4() (u *UUID, err error) {
100	u = new(UUID)
101	// Set all bits to randomly (or pseudo-randomly) chosen values.
102	_, err = rand.Read(u[:])
103	if err != nil {
104		return
105	}
106	u.setVariant(ReservedRFC4122)
107	u.setVersion(4)
108	return
109}
110
111// Generate a UUID based on the SHA-1 hash of a namespace identifier
112// and a name.
113func NewV5(ns *UUID, name []byte) (u *UUID, err error) {
114	u = new(UUID)
115	// Set all bits to truncated SHA1 hash generated from namespace
116	// and name.
117	u.setBytesFromHash(sha1.New(), ns[:], name)
118	u.setVariant(ReservedRFC4122)
119	u.setVersion(5)
120	return
121}
122
123// Generate a MD5 hash of a namespace and a name, and copy it to the
124// UUID slice.
125func (u *UUID) setBytesFromHash(hash hash.Hash, ns, name []byte) {
126	hash.Write(ns[:])
127	hash.Write(name)
128	copy(u[:], hash.Sum([]byte{})[:16])
129}
130
131// Set the two most significant bits (bits 6 and 7) of the
132// clock_seq_hi_and_reserved to zero and one, respectively.
133func (u *UUID) setVariant(v byte) {
134	switch v {
135	case ReservedNCS:
136		u[8] = (u[8] | ReservedNCS) & 0xBF
137	case ReservedRFC4122:
138		u[8] = (u[8] | ReservedRFC4122) & 0x7F
139	case ReservedMicrosoft:
140		u[8] = (u[8] | ReservedMicrosoft) & 0x3F
141	}
142}
143
144// Variant returns the UUID Variant, which determines the internal
145// layout of the UUID. This will be one of the constants: RESERVED_NCS,
146// RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE.
147func (u *UUID) Variant() byte {
148	if u[8]&ReservedNCS == ReservedNCS {
149		return ReservedNCS
150	} else if u[8]&ReservedRFC4122 == ReservedRFC4122 {
151		return ReservedRFC4122
152	} else if u[8]&ReservedMicrosoft == ReservedMicrosoft {
153		return ReservedMicrosoft
154	}
155	return ReservedFuture
156}
157
158// Set the four most significant bits (bits 12 through 15) of the
159// time_hi_and_version field to the 4-bit version number.
160func (u *UUID) setVersion(v byte) {
161	u[6] = (u[6] & 0xF) | (v << 4)
162}
163
164// Version returns a version number of the algorithm used to
165// generate the UUID sequence.
166func (u *UUID) Version() uint {
167	return uint(u[6] >> 4)
168}
169
170// Returns unparsed version of the generated UUID sequence.
171func (u *UUID) String() string {
172	return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
173}
174