...

Source file src/github.com/Microsoft/go-winio/pkg/guid/guid.go

Documentation: github.com/Microsoft/go-winio/pkg/guid

     1  // Package guid provides a GUID type. The backing structure for a GUID is
     2  // identical to that used by the golang.org/x/sys/windows GUID type.
     3  // There are two main binary encodings used for a GUID, the big-endian encoding,
     4  // and the Windows (mixed-endian) encoding. See here for details:
     5  // https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
     6  package guid
     7  
     8  import (
     9  	"crypto/rand"
    10  	"crypto/sha1" //nolint:gosec // not used for secure application
    11  	"encoding"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"strconv"
    15  )
    16  
    17  //go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment
    18  
    19  // Variant specifies which GUID variant (or "type") of the GUID. It determines
    20  // how the entirety of the rest of the GUID is interpreted.
    21  type Variant uint8
    22  
    23  // The variants specified by RFC 4122 section 4.1.1.
    24  const (
    25  	// VariantUnknown specifies a GUID variant which does not conform to one of
    26  	// the variant encodings specified in RFC 4122.
    27  	VariantUnknown Variant = iota
    28  	VariantNCS
    29  	VariantRFC4122 // RFC 4122
    30  	VariantMicrosoft
    31  	VariantFuture
    32  )
    33  
    34  // Version specifies how the bits in the GUID were generated. For instance, a
    35  // version 4 GUID is randomly generated, and a version 5 is generated from the
    36  // hash of an input string.
    37  type Version uint8
    38  
    39  func (v Version) String() string {
    40  	return strconv.FormatUint(uint64(v), 10)
    41  }
    42  
    43  var _ = (encoding.TextMarshaler)(GUID{})
    44  var _ = (encoding.TextUnmarshaler)(&GUID{})
    45  
    46  // NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
    47  func NewV4() (GUID, error) {
    48  	var b [16]byte
    49  	if _, err := rand.Read(b[:]); err != nil {
    50  		return GUID{}, err
    51  	}
    52  
    53  	g := FromArray(b)
    54  	g.setVersion(4) // Version 4 means randomly generated.
    55  	g.setVariant(VariantRFC4122)
    56  
    57  	return g, nil
    58  }
    59  
    60  // NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
    61  // GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
    62  // and the sample code treats it as a series of bytes, so we do the same here.
    63  //
    64  // Some implementations, such as those found on Windows, treat the name as a
    65  // big-endian UTF16 stream of bytes. If that is desired, the string can be
    66  // encoded as such before being passed to this function.
    67  func NewV5(namespace GUID, name []byte) (GUID, error) {
    68  	b := sha1.New() //nolint:gosec // not used for secure application
    69  	namespaceBytes := namespace.ToArray()
    70  	b.Write(namespaceBytes[:])
    71  	b.Write(name)
    72  
    73  	a := [16]byte{}
    74  	copy(a[:], b.Sum(nil))
    75  
    76  	g := FromArray(a)
    77  	g.setVersion(5) // Version 5 means generated from a string.
    78  	g.setVariant(VariantRFC4122)
    79  
    80  	return g, nil
    81  }
    82  
    83  func fromArray(b [16]byte, order binary.ByteOrder) GUID {
    84  	var g GUID
    85  	g.Data1 = order.Uint32(b[0:4])
    86  	g.Data2 = order.Uint16(b[4:6])
    87  	g.Data3 = order.Uint16(b[6:8])
    88  	copy(g.Data4[:], b[8:16])
    89  	return g
    90  }
    91  
    92  func (g GUID) toArray(order binary.ByteOrder) [16]byte {
    93  	b := [16]byte{}
    94  	order.PutUint32(b[0:4], g.Data1)
    95  	order.PutUint16(b[4:6], g.Data2)
    96  	order.PutUint16(b[6:8], g.Data3)
    97  	copy(b[8:16], g.Data4[:])
    98  	return b
    99  }
   100  
   101  // FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
   102  func FromArray(b [16]byte) GUID {
   103  	return fromArray(b, binary.BigEndian)
   104  }
   105  
   106  // ToArray returns an array of 16 bytes representing the GUID in big-endian
   107  // encoding.
   108  func (g GUID) ToArray() [16]byte {
   109  	return g.toArray(binary.BigEndian)
   110  }
   111  
   112  // FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
   113  func FromWindowsArray(b [16]byte) GUID {
   114  	return fromArray(b, binary.LittleEndian)
   115  }
   116  
   117  // ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
   118  // encoding.
   119  func (g GUID) ToWindowsArray() [16]byte {
   120  	return g.toArray(binary.LittleEndian)
   121  }
   122  
   123  func (g GUID) String() string {
   124  	return fmt.Sprintf(
   125  		"%08x-%04x-%04x-%04x-%012x",
   126  		g.Data1,
   127  		g.Data2,
   128  		g.Data3,
   129  		g.Data4[:2],
   130  		g.Data4[2:])
   131  }
   132  
   133  // FromString parses a string containing a GUID and returns the GUID. The only
   134  // format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
   135  // format.
   136  func FromString(s string) (GUID, error) {
   137  	if len(s) != 36 {
   138  		return GUID{}, fmt.Errorf("invalid GUID %q", s)
   139  	}
   140  	if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
   141  		return GUID{}, fmt.Errorf("invalid GUID %q", s)
   142  	}
   143  
   144  	var g GUID
   145  
   146  	data1, err := strconv.ParseUint(s[0:8], 16, 32)
   147  	if err != nil {
   148  		return GUID{}, fmt.Errorf("invalid GUID %q", s)
   149  	}
   150  	g.Data1 = uint32(data1)
   151  
   152  	data2, err := strconv.ParseUint(s[9:13], 16, 16)
   153  	if err != nil {
   154  		return GUID{}, fmt.Errorf("invalid GUID %q", s)
   155  	}
   156  	g.Data2 = uint16(data2)
   157  
   158  	data3, err := strconv.ParseUint(s[14:18], 16, 16)
   159  	if err != nil {
   160  		return GUID{}, fmt.Errorf("invalid GUID %q", s)
   161  	}
   162  	g.Data3 = uint16(data3)
   163  
   164  	for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
   165  		v, err := strconv.ParseUint(s[x:x+2], 16, 8)
   166  		if err != nil {
   167  			return GUID{}, fmt.Errorf("invalid GUID %q", s)
   168  		}
   169  		g.Data4[i] = uint8(v)
   170  	}
   171  
   172  	return g, nil
   173  }
   174  
   175  func (g *GUID) setVariant(v Variant) {
   176  	d := g.Data4[0]
   177  	switch v {
   178  	case VariantNCS:
   179  		d = (d & 0x7f)
   180  	case VariantRFC4122:
   181  		d = (d & 0x3f) | 0x80
   182  	case VariantMicrosoft:
   183  		d = (d & 0x1f) | 0xc0
   184  	case VariantFuture:
   185  		d = (d & 0x0f) | 0xe0
   186  	case VariantUnknown:
   187  		fallthrough
   188  	default:
   189  		panic(fmt.Sprintf("invalid variant: %d", v))
   190  	}
   191  	g.Data4[0] = d
   192  }
   193  
   194  // Variant returns the GUID variant, as defined in RFC 4122.
   195  func (g GUID) Variant() Variant {
   196  	b := g.Data4[0]
   197  	if b&0x80 == 0 {
   198  		return VariantNCS
   199  	} else if b&0xc0 == 0x80 {
   200  		return VariantRFC4122
   201  	} else if b&0xe0 == 0xc0 {
   202  		return VariantMicrosoft
   203  	} else if b&0xe0 == 0xe0 {
   204  		return VariantFuture
   205  	}
   206  	return VariantUnknown
   207  }
   208  
   209  func (g *GUID) setVersion(v Version) {
   210  	g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
   211  }
   212  
   213  // Version returns the GUID version, as defined in RFC 4122.
   214  func (g GUID) Version() Version {
   215  	return Version((g.Data3 & 0xF000) >> 12)
   216  }
   217  
   218  // MarshalText returns the textual representation of the GUID.
   219  func (g GUID) MarshalText() ([]byte, error) {
   220  	return []byte(g.String()), nil
   221  }
   222  
   223  // UnmarshalText takes the textual representation of a GUID, and unmarhals it
   224  // into this GUID.
   225  func (g *GUID) UnmarshalText(text []byte) error {
   226  	g2, err := FromString(string(text))
   227  	if err != nil {
   228  		return err
   229  	}
   230  	*g = g2
   231  	return nil
   232  }
   233  

View as plain text