dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.98k stars 4.66k forks source link

Proposal : Full RFC 4122 for Guids #23868

Closed sandorfr closed 3 years ago

sandorfr commented 6 years ago

Description

Provide full implementation of RFC 4211 for UUID as part of the Guid type.

Having hash based predictible Guids (v5) which could be derived from string and don't collide with random based guids (v4) can be extremely useful in some scenarios. RFC4122 proposes a standard around UUIDs to address that.

Possible Use Cases

Here are some of the use cases I see:

Proposed API

public partial struct Guid {
    public static readonly Guid DnsNamespace // 6ba7b810-9dad-11d1-80b4-00c04fd430c8
    public static readonly Guid UrlNamespace // 6ba7b811-9dad-11d1-80b4-00c04fd430c8

    public static Guid NewGuid();

    public static Guid NewUuidV1();
    public static Guid NewUuidV4();
    public static Guid NewUuidV5(string value, Guid namespace);
    public static Guid NewUuidV5(byte[] value, Guid namespace);
}

Details

We would obviously keep the existing NewGuid() which in windows calls UuidCreate. It seems that it uses UUID v4 under the hood.

In addition we would add NewGuidV1, NewGuidV4 et NewGuidV5 methods. They would map to the adequate UUID generation method.

Related Issues

Open Questions

Currently the byte order from the constructor and ToByteArray is a bit unatural as it just reflect the underlying struct as we could expect a more natural byte order. It could be worth it to add methods overload to read and write guid using the natural order.

See also

Updates

joperezr commented 6 years ago

@sandorfr are you interested on writing up a formal api proposal so that we can take a look at it?

sandorfr commented 6 years ago

Ok I’ll give a try at it :) On Wed 22 Nov 2017 at 09:16, Jose Perez Rodriguez notifications@github.com wrote:

@sandorfr https://github.com/sandorfr are you interested on writing up a formal api proposal so that we can take a look at it?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dotnet/corefx/issues/24680#issuecomment-346178966, or mute the thread https://github.com/notifications/unsubscribe-auth/ACiXplOzC6M1YvE6BKEpDE2luHz0Rc-Nks5s40u7gaJpZM4P7g0s .

sandorfr commented 6 years ago

Just updated the description accordingly.

joperezr commented 6 years ago

@sandorfr from your proposal can be extremely useful in some scenarios can you please add some sample scenarios where this would be useful to help on the review?

sandorfr commented 6 years ago

@joperezr I've added a section with possible use cases

shmuelie commented 5 years ago

In a project I had a need for a V4 UUID (implementation of an internal communication protocal). In my research I found that while System.Guid is a V4 UUID there is no guarantee that this will always be true. I therefore built my own Uuid type. While I can't say it's fully optimized, it is full compliant with RFC 4122 and provides logic for creating versions 2, 3, 4, and 5. I've copied the API below. (The implementation uses unsafe code in some places to prevent creating arrays.)


    /// <summary>
    ///     An Universally Unique IDentifier
    /// </summary>
    /// <remarks>
    ///     <para>Represents a RFC-4122 compliant Universally Unique IDentifier (UUID). An UUID is a 128-bit integer (16 bytes) that can be used across all computers and networks wherever a unique identifier is required. Such an identifier has a very low probability of being duplicated.</para>
    ///     <para>A <see cref="Guid"/> is a <see cref="Uuid"/>, version 4.</para>
    /// </remarks>
    /// <seealso cref="IEquatable{T}" />
    /// <seealso cref="IComparable{T}"/>
    /// <seealso cref="IComparable"/>
    /// <seealso cref="Guid"/>
    /// <seealso href="https://tools.ietf.org/html/rfc4122">RFC-4122</seealso>
    [StructLayout(LayoutKind.Sequential)]
    [Serializable]
    [DebuggerDisplay("{DebuggerDisplay,nq}")]
    public readonly struct Uuid : IEquatable<Uuid>, IComparable<Uuid>, IComparable
    {
        /// <summary>
        ///     A UUID with all bits set to zero.
        /// </summary>
        /// <value>00000000-0000-0000-0000-000000000000</value>
        /// <remarks><para>Same as <see cref="Uuid.Uuid()"/></para></remarks>
        public static readonly Uuid Nil = new Uuid();

        /// <summary>
        ///     Name string is a fully-qualified domain name
        /// </summary>
        /// <value>6ba7b810-9dad-11d1-80b4-00c04fd430c8</value>
        public static readonly Uuid DNS = new Uuid(0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c0, 0x4fd430c8);

        /// <summary>
        ///     Name string is a URL.
        /// </summary>
        /// <value>6ba7b811-9dad-11d1-80b4-00c04fd430c8</value>
        public static readonly Uuid Url = new Uuid(0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c0, 0x4fd430c8);

        /// <summary>
        ///     Name string is an ISO OID.
        /// </summary>
        /// <value>6ba7b812-9dad-11d1-80b4-00c04fd430c8</value>
        public static readonly Uuid Oid = new Uuid(0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c0, 0x4fd430c8);

        /// <summary>
        ///     Name string is an X.500 DN (in DER or a text output format).
        /// </summary>
        /// <value>6ba7b814-9dad-11d1-80b4-00c04fd430c8</value>
        public static readonly Uuid X500 = new Uuid(0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c0, 0x4fd430c8);

        /// <summary>
        ///     Initializes a new instance of the <see cref="Uuid"/> struct.
        /// </summary>
        /// <param name="time_low">The low field of the timestamp.</param>
        /// <param name="time_mid">The middle field of the timestamp.</param>
        /// <param name="time_hi_and_version">The high field of the timestamp multiplexed with the version number.</param>
        /// <param name="clock_seq_hi_and_reserved">The high field of the clock sequence multiplexed with the variant.</param>
        /// <param name="clock_seq_low">The low field of the clock sequence.</param>
        /// <param name="node_hi">The first two bytes of the spatially unique node identifier.</param>
        /// <param name="node_low">The last four bytes of the spatially unique node identifier.</param>
        private Uuid(uint time_low, ushort time_mid, ushort time_hi_and_version, byte clock_seq_hi_and_reserved, byte clock_seq_low, ushort node_hi, uint node_low);

        /// <summary>
        ///     Initializes a new instance of the <see cref="Uuid"/> struct.
        /// </summary>
        /// <param name="time_low">The low field of the timestamp.</param>
        /// <param name="time_mid">The middle field of the timestamp.</param>
        /// <param name="time_hi_and_version">The high field of the timestamp multiplexed with the version number.</param>
        /// <param name="clock_seq_hi_and_reserved">The high field of the clock sequence multiplexed with the variant.</param>
        /// <param name="clock_seq_low">The low field of the clock sequence.</param>
        /// <param name="node">The spatially unique node identifier.</param>
        private unsafe Uuid(uint time_low, ushort time_mid, ushort time_hi_and_version, byte clock_seq_hi_and_reserved, byte clock_seq_low, byte[] node);

        /// <summary>
        ///     Initializes a new instance of the <see cref="Uuid"/> struct.
        /// </summary>
        /// <param name="bytes">A 16-element byte array containing values with which to initialize the UUID.</param>
        private unsafe Uuid(byte[] bytes);

        /// <summary>
        ///     Initializes a new instance of the <see cref="Uuid"/> struct.
        /// </summary>
        /// <param name="bytes">A 16-element byte array containing values with which to initialize the UUID.</param>
        /// <param name="time_hi_and_version">The high field of the timestamp multiplexed with the version number.</param>
        /// <param name="clock_seq_hi_and_reserved">The high field of the clock sequence multiplexed with the variant.</param>
        /// <remarks><para>Bytes 6-8 (zero indexed) of <paramref name="bytes"/> are ignored. <paramref name="clock_seq_hi_and_reserved"/> and <paramref name="time_hi_and_version"/> are used instead.</para></remarks>
        private unsafe Uuid(byte[] bytes, ushort time_hi_and_version, byte clock_seq_hi_and_reserved);

        /// <summary>
        ///     Creates a new truly-random or pseudo-random (version 4) UUID.
        /// </summary>
        /// <returns>A new UUID.</returns>
        public static Uuid CreateRandom();

        /// <summary>
        ///     Creates a new time-based (version 1) UUID.
        /// </summary>
        /// <returns>A new UUID.</returns>
        public static Uuid CreateTimeBased();

        /// <summary>
        ///     Returns a 16-element byte array that contains the value of this instance.
        /// </summary>
        /// <returns>A 16-element byte array.</returns>
        /// <remarks><para>Note that the order of bytes returned is network order, regardless of the system.</para></remarks>
        public unsafe byte[] ToByteArray();

        /// <summary>
        ///     Creates a new MD5 name-based (version 3) UUID.
        /// </summary>
        /// <param name="namespaceID">The namespace UUID.</param>
        /// <param name="name">The name for the new UUID.</param>
        /// <returns>A new UUID.</returns>
        public static Uuid CreateMd5NameBased(in Uuid namespaceID, string name);

        /// <summary>
        ///     Creates a new SHA1 name-based (version 5) UUID.
        /// </summary>
        /// <param name="namespaceID">The namespace UUID.</param>
        /// <param name="name">The name for the new UUID.</param>
        /// <returns>A new UUID.</returns>
        public static Uuid CreateSha1NameBased(in Uuid namespaceID, string name);

        /// <summary>
        ///     Gets the version.
        /// </summary>
        /// <value>
        ///     The version.
        /// </value>
        /// <remarks>
        ///     <list type="table">
        ///         <listheader>
        ///             <term>Version</term>
        ///             <description>Description</description>
        ///         </listheader>
        ///         <item>
        ///             <term>1</term>
        ///             <description>The time-based version.</description>
        ///         </item>
        ///         <item>
        ///             <term>2</term>
        ///             <description>DCE Security version, with embedded POSIX UIDs.</description>
        ///         </item>
        ///         <item>
        ///             <term>3</term>
        ///             <description>The name-based version that uses MD5 hashing.</description>
        ///         </item>
        ///         <item>
        ///             <term>4</term>
        ///             <description>The randomly or pseudo-randomly generated version.</description>
        ///         </item>
        ///         <item>
        ///             <term>5</term>
        ///             <description>The name-based version that uses SHA-1 hashing.</description>
        ///         </item>
        ///     </list>
        /// </remarks>
        public int Version { get; }

        /// <summary>
        ///     Gets the timestamp.
        /// </summary>
        /// <value>
        ///     The timestamp.
        /// </value>
        /// <remarks>
        ///     <para>For UUID version 1, this is represented by Coordinated Universal Time (UTC) as a count of 100-nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar) in a 60-bit value.</para>
        ///     <para>For UUID version 3 or 5, the timestamp is a 60-bit value constructed from a name.</para>
        ///     <para>For UUID version 4, the timestamp is a randomly or pseudo-randomly generated 60-bit value.</para>
        /// </remarks>
        public long Timestamp { get; }

        /// <summary>
        ///     Gets the clock sequence.
        /// </summary>
        /// <value>
        ///     The clock sequence.
        /// </value>
        /// <remarks>
        ///     <para>For UUID version 1, the clock sequence is used to help avoid duplicates that could arise when the clock is set backwards in time or if the node ID changes.</para>
        ///     <para>For UUID version 3 or 5, the clock sequence is a 14-bit value constructed from a name.</para>
        ///     <para>For UUID version 4, clock sequence is a randomly or pseudo-randomly generated 14-bit value.</para>
        /// </remarks>
        public short ClockSequence { get; }

        /// <summary>
        ///     Gets the node.
        /// </summary>
        /// <value>
        ///     The node.
        /// </value>
        /// <remarks>
        ///     <para>For UUID version 1, the node field consists of an IEEE 802 MAC address, usually the host address.  For systems with multiple IEEE 802 addresses, any available one can be used.  The lowest addressed octet (octet number 10) contains the global/local bit and the unicast/multicast bit, and is the first octet of the address transmitted on an 802.3 LAN.</para>
        ///     <para>For systems with no IEEE address, a randomly or pseudo-randomly generated value may be used.  The multicast bit must be set in such addresses, in order that they will never conflict with addresses obtained from network cards.</para>
        ///     <para>For UUID version 3 or 5, the node field is a 48-bit value constructed from a name.</para>
        ///     <para>For UUID version 4, the node field is a randomly or pseudo-randomly generated 48-bit value.</para>
        /// </remarks>
        public long Node { get; }

        /// <summary>
        ///     Converts to string.
        /// </summary>
        /// <returns>
        ///     A <see cref="string" /> that represents this instance.
        /// </returns>
        public override string ToString();

        /// <summary>
        ///     Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        ///     <see langword="true"/> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <see langword="false"/>.
        /// </returns>
        public bool Equals(Uuid other);

        /// <summary>
        ///     Determines whether the specified <see cref="object" />, is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="object" /> to compare with this instance.</param>
        /// <returns>
        ///     <see langword="true" /> if the specified <see cref="object" /> is equal to this instance; otherwise, <see langword="false" />.
        /// </returns>
        public override bool Equals(object obj);

        /// <summary>
        ///     Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        ///     A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
        /// </returns>
        public override int GetHashCode();

        /// <summary>
        ///     Converts the string representation of a UUID to the equivalent <see cref="Uuid"/> structure.
        /// </summary>
        /// <param name="input">The UUID to convert.</param>
        /// <param name="result">The structure that will contain the parsed value. If the method returns <see langword="true"/>, <paramref name="result"/> contains a valid <see cref="Uuid"/>. If the method returns <see langword="false"/>, <paramref name="result"/> equals <see cref="Nil"/>.</param>
        /// <returns><see langword="true"/> if the parse operation was successful; otherwise, <see langword="false"/>.</returns>
        public static bool TryParse(string input, out Uuid result);

        /// <summary>
        ///     Implements the operator ==.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>
        ///     The result of the operator.
        /// </returns>
        public static bool operator ==(Uuid left, Uuid right);

        /// <summary>
        ///     Implements the operator !=.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>
        ///     The result of the operator.
        /// </returns>
        public static bool operator !=(Uuid left, Uuid right);

        /// <summary>
        ///     Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
        /// </summary>
        /// <param name="other">An object to compare with this instance.</param>
        /// <returns>
        ///     <para>A value that indicates the relative order of the objects being compared. The return value has these meanings:</para>
        ///     <list type="table">
        ///         <listheader>
        ///             <term>Value</term>
        ///             <description>Meaning</description>
        ///         </listheader>
        ///         <item>
        ///             <term>Less than zero</term>
        ///             <description>This instance precedes <paramref name="other"/> in the sort order.</description>
        ///         </item>
        ///         <item>
        ///             <term>Zero</term>
        ///             <description>This instance occurs in the same position in the sort order as <paramref name="other"/>.</description>
        ///         </item>
        ///         <item>
        ///             <term>Greater than zero</term>
        ///             <description>This instance follows <paramref name="other"/> in the sort order.</description>
        ///         </item>
        ///     </list>
        /// </returns>
        /// <remarks><para>Lexical ordering is not temporal ordering!</para></remarks>
        public int CompareTo(Uuid other);

        /// <summary>
        ///     Compares this instance to a specified object and returns an indication of their relative values.
        /// </summary>
        /// <param name="obj">An object to compare, or <see langword="null"/>.</param>
        /// <returns>
        ///     <para>A signed number indicating the relative values of this instance and <paramref name="obj"/>.</para>
        ///     <list type="table">
        ///         <listheader>
        ///             <term>Return value</term>
        ///             <description>Description</description>
        ///         </listheader>
        ///         <item>
        ///             <term>A negative integer</term>
        ///             <description>This instance is less than <paramref name="obj"/>.</description>
        ///         </item>
        ///         <item>
        ///             <term>Zero</term>
        ///             <description>This instance is equal to <paramref name="obj"/>.</description>
        ///         </item>
        ///         <item>
        ///             <term>A positive integer</term>
        ///             <description>This instance is greater than <paramref name="obj"/>, or <paramref name="obj"/> is <see langword="null"/>.</description>
        ///         </item>
        ///     </list>
        /// </returns>
        /// <exception cref="ArgumentException"><paramref name="obj"/> is not a <see cref="Uuid"/>.</exception>
        public int CompareTo(object obj);

        /// <summary>
        ///     Implements the operator &gt;.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>
        ///     The result of the operator.
        /// </returns>
        public static bool operator >(Uuid left, Uuid right);

        /// <summary>
        ///     Implements the operator &lt;.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>
        ///     The result of the operator.
        /// </returns>
        public static bool operator <(Uuid left, Uuid right);

        /// <summary>
        ///     Implements the operator &gt;=.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>
        ///     The result of the operator.
        /// </returns>
        public static bool operator >=(Uuid left, Uuid right);

        /// <summary>
        ///     Implements the operator &lt;=.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>
        ///     The result of the operator.
        /// </returns>
        public static bool operator <=(Uuid left, Uuid right);

        /// <summary>
        ///     Performs an implicit conversion from <see cref="Guid"/> to <see cref="Uuid"/>.
        /// </summary>
        /// <param name="guid">The unique identifier.</param>
        /// <returns>
        ///     The result of the conversion.
        /// </returns>
        public static implicit operator Uuid(Guid guid);

        /// <summary>
        ///     Performs an explicit conversion from <see cref="Uuid"/> to <see cref="Guid"/>.
        /// </summary>
        /// <param name="uuid">The UUID.</param>
        /// <returns>
        ///     The result of the conversion.
        /// </returns>
        /// <exception cref="InvalidCastException">Only random (version 4) UUIDs can be converted to GUIDs.</exception>
        public static explicit operator Guid(Uuid uuid);

        /// <summary>
        ///     Gets the debugger display text.
        /// </summary>
        /// <value>
        ///     The debugger display text.
        /// </value>
        /// <remarks><para>Gives nicer display for pre-defined UUIDs.</para></remarks>
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private string DebuggerDisplay { get; }
    }
}
om-ha commented 4 years ago

For reference, found a good implementation here

om-ha commented 4 years ago

Please note version 4 requires specific bitwise manipulation. Also to be cryptographically secure it must not be generated with Guid.NewGuid();

sandorfr commented 4 years ago

For reference, found a good implementation here

Nice. The real problem here is to get the proposal/API approved to make this a thing :)

om-ha commented 4 years ago

@sandorfr In fact I ended up using GuidOne. Which accounts for Endianness unlike other libraries. It completed my Stackoverflow answer here.

shmuelie commented 4 years ago

I'm happy to provide my implementation if wanted.

GrabYourPitchforks commented 4 years ago

Thanks all for the discussion. Some random thoughts on this:

Given all of these I'm inclined to say that we should close this issue. If there's evidence that there's significant community need for this functionality and that it's too onerous for devs to roll the implementation themselves, then we should reconsider. But so far I don't think either threshold is met.

PSanetra commented 4 years ago

I don't think we should add any surface area to the Guid type. If we add factory methods, it's going to add confusion. "What's the difference between all of these, and which one should I use?" is going to come up.

I think that is just a reason to document those new methods.

For the same reason, I wouldn't add a Uuid type. The range of valid Guid and Uuid values would be identical, so IMO there's no reason to have two exchange types. It would again lead to confusion.

I agree

Creation of a v5 UUID involves using the SHA-1 hash algorithm. Introducing new code paths that leverage SHA-1 is generally forbidden across Microsoft codebases unless it's needed for some core compatibility or interop scenario. The demand for version 5 UUIDs is not high enough to justify an exception here.

I guess the reason behind avoiding SHA-1 is that it is partially broken for cryptographic use cases. In case of creating UUID v5 this seems to be irrelevant as the hash is truncated to 128 bit anyway for creating the UUID.

shmuelie commented 4 years ago

I don't think we should add any surface area to the Guid type. If we add factory methods, it's going to add confusion. "What's the difference between all of these, and which one should I use?" is going to come up.

Personally, I agree

For the same reason, I wouldn't add a Uuid type. The range of valid Guid and Uuid values would be identical, so IMO there's no reason to have two exchange types. It would again lead to confusion.

I disagree. This is a square and rectangle case. All Guids are valid Uuids but not all Uuids are valid Guids.

Creation of a v5 UUID involves using the SHA-1 hash algorithm. Introducing new code paths that leverage SHA-1 is generally forbidden across Microsoft codebases unless it's needed for some core compatibility or interop scenario. The demand for version 5 UUIDs is not high enough to justify an exception here.

v3 UUID is created using MD5, another cracked algorithm. I would argue that since in both cases it is not being used for cryptography but ID usage.

Given all of these I'm inclined to say that we should close this issue. If there's evidence that there's significant community need for this functionality and that it's too onerous for devs to roll the implementation themselves, then we should reconsider. But so far I don't think either threshold is met.

How often this is needed I can't say, but implementing correctly can be a pain, even more so if you must do so in a performant way.

sandorfr commented 4 years ago

I don't think we should add any surface area to the Guid type. If we add factory methods, it's going to add confusion. "What's the difference between all of these, and which one should I use?" is going to come up.

I strongly disagree with this one. That's a terrible reason not to offer an API. Yes there will be people who can't read the docs, it's not for them that you should design things.

Introducing new code paths that leverage SHA-1 is generally forbidden across Microsoft codebases unless it's needed for some core compatibility or interop scenario.

SHA-1 vulnerability in the context of cryptography really is irrelevant here.

If there's evidence that there's significant community need for this functionality and that it's too onerous for devs to roll the implementation themselves, then we should reconsider. But so far I don't think either threshold is met.

Given how long it's been opened and the low activity and reactions on this, I agree with this one. Also there's some decent implementations in some packages mentioned above.

GrabYourPitchforks commented 4 years ago

I would argue that since in both cases it is not being used for cryptography but ID usage.

SHA-1 vulnerability in the context of cryptography really is irrelevant here.

@SamuelEnglard @sandorfr I understand the arguments here, but "it's not used for cryptographic purposes" is not a valid argument for granting an exception. "Broken" algorithms like SHA1 are forbidden for all purposes across Microsoft code bases unless it's needed for legacy compat reasons or to support some core scenario for the product. (That last category is generally for allowing things like networking protocols.)

Unless we can demonstrate clearly that usage of MD5 or SHA1 in UUID generation is a critical scenario that we absolutely must support, we would not get signoff from release management to ship this functionality.

shmuelie commented 4 years ago

I understand the arguments here, but "it's not used for cryptographic purposes" is not a valid argument for granting an exception. "Broken" algorithms like SHA1 are forbidden for all purposes across Microsoft code bases unless it's needed for legacy compat reasons or to support some core scenario for the product. (That last category is generally for allowing things like networking protocols.)

I can't say I agree but at the same time I'm not the person who implemented these rules.

Also there's some decent implementations in some packages mentioned above.

Getting approval to include a dependency for exactly one type isn't always easy. If it comes as part of .NET though, no arguing needed.

KalleOlaviNiemitalo commented 4 years ago

.NET Runtime already implements a SHA-1 GUID algorithm as part of EventSource, albeit with a hardcoded namespace. I guess that is justified by legacy compat. https://github.com/dotnet/runtime/blob/53976d38b1bd6917b8fa4d1dd4f009728ece3adb/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs#L1525-L1714

GrabYourPitchforks commented 4 years ago

I guess that is justified by legacy compat.

Correct. You have no idea the sheer amount of paperwork we have to deal with every single release because of this and other compat-only usages. There's an increasing amount of bureaucracy every year. I think the goal is to make it so obnoxious that at a certain point we relent and say "fine, we're yanking it!" :)

mdonoughe commented 3 years ago

Note: the EventSource implementation of this algorithm is not compliant with the RFC. It generates UUIDs that match ETW, which is what matters for EventSource, but it does not generate UUIDs that match standard implementations. The problems are related to endianness of the byte arrays and not setting the RFC variant flag.

GrabYourPitchforks commented 3 years ago

Triaged this morning with the area owners. We don't believe this belongs in the BCL, and as mentioned in this thread there are good fully-featured packages in the ecosystem which provide this capability.