hardPass
hardPass

Reputation: 20821

Is there a method to generate a UUID with Go language?

I have code that looks like this:

u := make([]byte, 16)
_, err := rand.Read(u)
if err != nil {
    return
}

u[8] = (u[8] | 0x80) & 0xBF // what does this do?
u[6] = (u[6] | 0x40) & 0x4F // what does this do?

return hex.EncodeToString(u)

It returns a string with a length of 32, but I don't think it is a valid UUID. If it is a real UUID, why is it a UUID, and what is the purpose of the code that modifies the value of u[8] and u[6]?

Is there a better way of generating UUIDs?

Upvotes: 223

Views: 363317

Answers (15)

bmaupin
bmaupin

Reputation: 15995

gofrs/uuid is the replacement for satori/go.uuid, which was historically the most starred UUID package for Go. It currently (at the time of this writing) and historically has been actively maintained and supports the latest UUID standards.

import "github.com/gofrs/uuid"

// Create a Version 4 UUID, panicking on error
u := uuid.Must(uuid.NewV4())

Upvotes: 36

SillyMaxwell
SillyMaxwell

Reputation: 161

Random UUID using package from google:

package main

import (
    "fmt"

    "github.com/google/uuid"
)

func main() {
    out := uuid.Must(uuid.NewRandom()).String()
    fmt.Println(out)
}

Output:

a1c11a53-c4be-488f-89b6-f83bf2d48dab

Upvotes: 16

zangw
zangw

Reputation: 48386

The https://github.com/google/uuid module based on RFC 4122 and UUID V4 is currently supported. If you want to use the latest UUID version like UUID v7, This module https://github.com/uuid6/uuid6go-proto could be used.


UUID version 7: An entirely new time-based UUID bit layout sourced from the widely implemented and well known Unix Epoch timestamp source.

unix_ts_ms|ver|rand_a|var|rand_b 

Since the UUID V4 contains random and it is not used for sorting. However, there is a timestamp part on UUID V7, it would be more friendly for sorting.


Sample

    var gen uuid.UUIDv7Generator

    uuid := gen.Next()
    fmt.Println(uuid.ToString())
    fmt.Println(uuid.Time())
    fmt.Println(uuid.Timestamp())

Output

0632933765-357c-31b6-ed56-0daba726b1
2022-09-20 11:28:54 +0800 CST
1663644534

Upvotes: 2

Ankit Mishra
Ankit Mishra

Reputation: 590

So you asked:
Q1. u[8] = (u[8] | 0x80) & 0xBF // what does this do?
Ans: This section defines variant. You can learn more about it from https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.1

Q2. u[6] = (u[6] | 0x40) & 0x4F // what does this do?
Ans: We show version number with the most 4 significant bits so in this case version 4 so we want to set it with "0100". Version 4 is most widely used UUID and its based on random bits generation. It uses 128 bits, Out of which 4 bits are fixed to tell version number and 2 bits are fixed to tell variant. So we have 122 bits left which can be randomly generated.

You can generate UUID v4 by importing package from Google:
https://github.com/google/uuid

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    uuid := uuid.New()
    fmt.Println(uuid.String())
}

Also, you can try package I created. It's very light weight and easy to understand. https://github.com/bitactro/UUIDv4

package main

import (
    "fmt"
    "github.com/bitactro/UUIDv4"
)

func main() {
    
    fmt.Println(uuidv4.GenerateUUID4())
}

Upvotes: 3

Ken Cloud
Ken Cloud

Reputation: 933

"crypto/rand" is cross platform pkg for random bytes generattion

package main

import (
    "crypto/rand"
    "fmt"
)

// Note - NOT RFC4122 compliant
func pseudo_uuid() (uuid string) {

    b := make([]byte, 16)
    _, err := rand.Read(b)
    if err != nil {
        fmt.Println("Error: ", err)
        return
    }

    uuid = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

    return
}

Upvotes: 65

shutefan
shutefan

Reputation: 6926

There is an official implementation by Google: https://github.com/google/uuid

Generating a version 4 UUID works like this:

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    id := uuid.New()
    fmt.Println(id.String())
}

Try it here: https://play.golang.org/p/6YPi1djUMj9

Upvotes: 243

eric chiang
eric chiang

Reputation: 2745

As part of the uuid spec, if you generate a uuid from random it must contain a "4" as the 13th character and a "8", "9", "a", or "b" in the 17th (source).

// this makes sure that the 13th character is "4"
u[6] = (u[6] | 0x40) & 0x4F
// this makes sure that the 17th is "8", "9", "a", or "b"
u[8] = (u[8] | 0x80) & 0xBF 

Upvotes: 8

soulshake
soulshake

Reputation: 969

On Linux, you can read from /proc/sys/kernel/random/uuid:

package main

import "io/ioutil"
import "fmt"

func main() {
    u, _ := ioutil.ReadFile("/proc/sys/kernel/random/uuid")
    fmt.Println(string(u))
}

No external dependencies!

$ go run uuid.go 
3ee995e3-0c96-4e30-ac1e-f7f04fd03e44

Upvotes: 8

twinj
twinj

Reputation: 2039

The go-uuid library is NOT RFC4122 compliant. The variant bits are not set correctly. There have been several attempts by community members to have this fixed but pull requests for the fix are not being accepted.

You can generate UUIDs using the Go uuid library I rewrote based on the go-uuid library. There are several fixes and improvements. This can be installed with:

go get github.com/twinj/uuid

You can generate random (version 4) UUIDs with:

import "github.com/twinj/uuid"

u := uuid.NewV4()

The returned UUID type is an interface and the underlying type is an array.

The library also generates v1 UUIDs and correctly generates v3 and 5 UUIDs. There are several new methods to help with printing and formatting and also new general methods to create UUIDs based off of existing data.

Upvotes: 80

peiiion
peiiion

Reputation: 298

The gorand package has a UUID method that returns a Version 4 (randomly generated) UUID in its canonical string representation ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") and it's RFC 4122 compliant.

It also uses the crypto/rand package to ensure the most cryptographically secure generation of UUIDs across all platforms supported by Go.

import "github.com/leonelquinteros/gorand"

func main() {
    uuid, err := gorand.UUID()
    if err != nil {
        panic(err.Error())
    }

    println(uuid)
} 

Upvotes: 4

James McGill
James McGill

Reputation: 104

This library is our standard for uuid generation and parsing:

https://github.com/pborman/uuid

Upvotes: 1

James Henstridge
James Henstridge

Reputation: 43899

You can generate UUIDs using the go-uuid library. This can be installed with:

go get github.com/nu7hatch/gouuid

You can generate random (version 4) UUIDs with:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

The returned UUID type is a 16 byte array, so you can retrieve the binary value easily. It also provides the standard hex string representation via its String() method.

The code you have also looks like it will also generate a valid version 4 UUID: the bitwise manipulation you perform at the end set the version and variant fields of the UUID to correctly identify it as version 4. This is done to distinguish random UUIDs from ones generated via other algorithms (e.g. version 1 UUIDs based on your MAC address and time).

Upvotes: 120

kostix
kostix

Reputation: 55453

For Windows, I did recently this:

// +build windows

package main

import (
    "syscall"
    "unsafe"
)

var (
    modrpcrt4 = syscall.NewLazyDLL("rpcrt4.dll")
    procUuidCreate = modrpcrt4.NewProc("UuidCreate")
)

const (
    RPC_S_OK = 0
)

func NewUuid() ([]byte, error) {
    var uuid [16]byte
    rc, _, e := syscall.Syscall(procUuidCreate.Addr(), 1,
             uintptr(unsafe.Pointer(&uuid[0])), 0, 0)
    if int(rc) != RPC_S_OK {
        if e != 0 {
            return nil, error(e)
        } else {
            return nil, syscall.EINVAL
        }
    }
    return uuid[:], nil
}

Upvotes: 1

zzzz
zzzz

Reputation: 91243

From Russ Cox's post:

There's no official library. Ignoring error checking, this seems like it would work fine:

f, _ := os.Open("/dev/urandom")
b := make([]byte, 16)
f.Read(b)
f.Close()
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

Note: In the original, pre Go 1 version the first line was:

f, _ := os.Open("/dev/urandom", os.O_RDONLY, 0)

Here it compiles and executes, only /dev/urandom returns all zeros in the playground. Should work fine locally.

In the same thread there are some other methods/references/packages found.

Upvotes: 12

jimt
jimt

Reputation: 26380

u[8] = (u[8] | 0x80) & 0xBF // what's the purpose ?
u[6] = (u[6] | 0x40) & 0x4F // what's the purpose ?

These lines clamp the values of byte 6 and 8 to a specific range. rand.Read returns random bytes in the range 0-255, which are not all valid values for a UUID. As far as I can tell, this should be done for all the values in the slice though.

If you are on linux, you can alternatively call /usr/bin/uuidgen.

package main

import (
    "fmt"
    "log"
    "os/exec"
)

func main() {
    out, err := exec.Command("uuidgen").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", out)
}

Which yields:

$ go run uuid.go 
dc9076e9-2fda-4019-bd2c-900a8284b9c4

Upvotes: 34

Related Questions