Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we decode a Guid to find out where and when was it generated?

Tags:

c#

.net

This article explains how Guids are generated.

My question is that is there any way to find out which machine in my web farm generated this Guid and when?

like image 216
Faisal Avatar asked Aug 23 '11 10:08

Faisal


1 Answers

Neil Fenwick is correct. However we can use the structure to our advantage.

Version 4 (.Net)

Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the version number as well as two reserved bits. All other bits are set using a random or pseudorandom data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479.

Use the Version Field

We are free to change the first nibble of byte 8; so if you have less than 17 machines you can identify them by altering the GUIDs created on each one.

static Guid NewSystemGuid(int machine)
{
    if (machine < 0 | machine > 0xF)
        throw new ArgumentOutOfRangeException("machine");
    var g = Guid.NewGuid();
    var arr = g.ToByteArray();
    arr[7] = (byte)((machine << 4) | (arr[7] & 0xF));
    return new Guid(arr);
}

static int ExtractMachine(Guid guid)
{
    var arr = guid.ToByteArray();
    return (arr[7] >> 4) & 0xF;
}

Use the Version Field and 'y'

I am not sure if changing Y will alter the uniqueness of the GUID, so your mileage may vary. If you have less than 17 machines stick with the first solution.

static Guid NewSystemGuid(int machine)
{
    if (machine < 0 | machine > 0xFF)
        throw new ArgumentOutOfRangeException("machine");

    var m1 = machine & 0xF;
    var m2 = (machine >> 4) & 0xF;

    var g = Guid.NewGuid();
    var arr = g.ToByteArray();
    arr[7] = (byte)((m1 << 4) | (arr[7] & 0xF));
    arr[8] = (byte)((m2 << 4) | (arr[8] & 0xF));
    return new Guid(arr);
}

static int ExtractMachine(Guid guid)
{
    var arr = guid.ToByteArray();
    return 
        ((arr[7] >> 4) & 0xF) |
        (((arr[8] >> 4) & 0xF) << 4);
}

Use the Version and 'y' (Redux)

You can still retain the value in 'y' by limiting the amount of machines to 63 (using the last 2 bits to represent the 4 possible values of 'y'):

static Guid NewSystemGuid(int machine)
{
    if (machine < 0 | machine > 0x3F)
        throw new ArgumentOutOfRangeException("machine");

    var m1 = machine & 0xF;
    var m2 = (machine >> 4) & 0xF;

    var g = Guid.NewGuid();
    var arr = g.ToByteArray();
    arr[7] = (byte)((m1 << 4) | (arr[7] & 0xF));

    var y = (arr[8] >> 4) & 0xF;
    switch (y)
    {
        case 0x8:
            arr[8] = (byte)((m2 << 4) | (arr[8] & 0xF));
            break;
        case 0x9:
            arr[8] = (byte)(((m2 | 0x8) << 4) | (arr[8] & 0xF));
            break;
        case 0xA:
            arr[8] = (byte)(((m2 | 0x4) << 4) | (arr[8] & 0xF));
            break;
        case 0xB:
            arr[8] = (byte)(((m2 | 0xC) << 4) | (arr[8] & 0xF));
            break;
        default:
            throw new Exception();
    }
    return new Guid(arr);
}

static int ExtractMachine(Guid guid)
{
    var arr = guid.ToByteArray();
    return 
        ((arr[7] >> 4) & 0xF) |
        (((arr[8] >> 4) & 0x3) << 4);
}

Use Version 1 GUIDs

You could also use version 1 GUIDs, as it's still possible to generate them:

class SequentialGuid
{
    [DllImport("rpcrt4.dll", SetLastError = true)]
    static extern int UuidCreateSequential(out Guid guid); 

    public static Guid NewGuid()
    {
        Guid guid;
        UuidCreateSequential(out guid);
        return guid;
    }

    public static byte[] ExtractMacAddress(Guid guid)
    {
        var arr = guid.ToByteArray();
        // Require version 1.
        if (((arr[7] >> 4) & 0xF) != 1)
            throw new ArgumentOutOfRangeException("guid", "GUID is required to be a sequential (version 1) GUID.");

        var macLong = BitConverter.ToInt64(arr, arr.Length - 8);
        macLong = IPAddress.NetworkToHostOrder(macLong);
        arr = BitConverter.GetBytes(macLong);
        Array.Resize(ref arr, 6);

        return arr;
    }
}
like image 127
Jonathan Dickinson Avatar answered Oct 06 '22 00:10

Jonathan Dickinson