package uuidx import ( "github.com/google/uuid" ) /* DetectMsGuid tries to guess if a given [uuid.UUID] is actually a Microsoft GUID or not. Note that there are technically *two* types of Microsoft GUIDs: * One is predictable, and defined in RFC 9562 [§ 4.2] as a known variant. Detecting this is very easy and (assuming an RFC-compliant UUID is originally passed) is detectable with 100% confidence. It's also legacy, and Microsoft no longer uses this format. Because they are insane and enjoy the suffering of others. * The other, MODERN Microsoft GUID currently in use is the endianness-flipped version (see [ToggleUuidMsGuid]). This is impossible to 100% determine, but analysis can get *pretty* close. cs is a confidence scoring. As more logic is added, it *is* mathematically possible (though unlikely) that cs == 0, so the caller is then responsible for making further guesswork based on contextual analysis ("Did I get this UUID/GUID from an Active Directory attribute?" "Is it a SID constant?" etc.). A score > 0 indicates a confidence leaning towards the provided UUID/GUID being a Microsoft GUID. A score < 0 indicates a confidence leaning towards the provided UUID/GUID *not* being a Microsoft GUID. Note that a score of < 0 does not necessarily indicate it is a *proper, standard RFC-compliant UUID*, simply that it is likely NOT a Microsoft GUID. [IsRfc] will be of further help in these cases. csFlip indicates a score for the [ToggleUuidMsGuid]-flipped version of u. It follows the same rules for thresholds and such as cs, but may be awarded different confidence levels internally due to different chances of false positives. If both cs and csFlip are > 0 but csFlip > cs, it is better to assume that u is *not* in the flipped-endian format but *is* a Microsoft GUID (in other words, it is likely that u has *already been flipped* to proper/consistent endianness instead of being a mixed-endian GUID). In some cases where flipped-endianness does not matter (e.g. [IsNilUUID], [IsMaxUUID]), cs and csFlip will be equal. *Randomly-generated* GUIDs on Windows Server 2000-family and up are almost always UUIDv4. Pre-Windows Server 2000 family *OR* any *statically-defined* GUIDs (schemaIDGUID, rightsGUID, CLSID constants, etc.) are all over the place - TYPICALLY UUIDv1, but it's nothing predictable enough to be useful in definitive classification. COM interfaces are all OVER the place in UUID version, but usually *not* UUIDv4. A target/expected UUID version can be provided via tgtVer. To disable version analysis, use 0 (or 0x00, etc.). It is *highly* recommended to provide a tgtVer if it is known; it can significantly boost confidence in the correct direction. A warning, though - if a *wrong* tgtVer IS specified, it can negatively affect confidence accuracy. Thus if you aren't ABSOLUTELY certain of the target UUID version, it's better to use 0/0x00 to disable the check. Providing a target version is key to breaking some ties (e.g. both cs and csFlip are equal). For example, the given RFC-compliant UUIDv4: 8d8e35ae-58d2-4d28-b09d-ffffffffffff when flipped evaluates to an RFC-compliant UUIDv2: ae358e8d-d258-284d-b09d-ffffffffffff and in this case, cs and csFlip will both end up as 0. Providing a tgtVer of 4 shifts this to a proper "tie-breaker" of cs == -3 and csFlip == 0. Similarly, the endian-flipped UUIDv4 evaluates as a UUIDv2: 9856ea36-c2ca-2347-af0c-3b42f76c9eca from the original unflipped UUIDv4: 36ea5698-cac2-4723-af0c-3b42f76c9eca which results in a cs == 1 and csFlip == 0 - not very high confidence (but at least a correct and non-zero lean). Providing a tgtVer == 4 changes this to cs == 7 and csFlip == 0, which is *much* more decisive. UUIDs/GUIDs found to be strictly RFC-conforming (via [IsRfc], which returns false for Microsoft GUIDs) are *heavily* weighted negatively. Confidence levels can be generally considered as the following: cs >= 7: Likely Microsoft GUID (mixed-endian) cs >= 4: Likely Microsoft GUID 0 < cs < 4: Leans Microsoft GUID, but untrusted cs == 0: Entirely ambiguous/indeterminate -4 < cs < 0: Leans UUID/non-Microsoft GUID but untrusted cs <= -5: Likely UUID/not Microsoft GUID csFlip >=cs && csFlip >= 4: Likely a pre-flipped (ToggleUuidMsGuid'd) Microsoft GUID [§ 4.2]: https://datatracker.ietf.org/doc/html/rfc9562#section-4.2 */ func DetectMsGuid(u uuid.UUID, tgtVer uuid.Version) (cs, csFlip int) { var isRfc bool var flippedRfc bool var flipped uuid.UUID = ToggleUuidMsGuid(u) // These are the exact same when flipped, and are statically defined. if IsNilUUID(u) || IsMaxUUID(u) { cs = -12 csFlip = -12 return } // Most/all(?) Microsoft GUIDs are not NCS. if IsNcs(u) { cs -= 2 } if IsNcs(flipped) { // The flipped has a higher likelihood of false-pos, so we don't score it as confidently. csFlip -= 1 } if u.Version() == 0 { if u.Variant() == uuid.Microsoft { cs += 10 } else { cs -= 2 } } if flipped.Version() == 0 { if flipped.Variant() == uuid.Microsoft { csFlip += 4 } else { csFlip -= 1 } } // Valid RFC version and variant. IsRfc returns false for the Microsoft Variant and version == 0. // Modern MS uses an RFC 4122 variant indicator but flips the endianness. isRfc, _ = IsRfc(u) flippedRfc, _ = IsRfc(flipped) if u.Variant() == uuid.RFC4122 { // This might be the strongest indicator. if isRfc && !flippedRfc { // This is *very* strong of being an MS GUID. cs -= 8 csFlip += 4 } else if !isRfc && flippedRfc { // It probably is an MS GUID but was already flipped. csFlip += 6 } else if isRfc && flippedRfc { /* If both are RFC-compat, it's a weird case where it actually IS RFC compliant and by chance the flipped is *also* RFC compat. An example of this is: 8d8e35ae-58d2-4d28-b09d-ffffffffffff Which has the flipped version of: ae358e8d-d258-284d-b09d-ffffffffffff The original is a v4, the flipped evaluates as a v2! Providing a target version breaks this away to a definitive score. */ } } // *HEAVILY* weigh a provided version. if tgtVer != 0 { // NCS does some weird things to the versioning field. We return early on it though. // MS GUIDs have a pretty small chance of matching, // but their flipped counterpart SHOULD match versions. if flipped.Version() == tgtVer { cs += 7 } else { cs -= 3 } } else { // Give a *very small* boost to flippedRfc and flipped.Version() == 4, since it's so common. // Don't make this too high though since the version is explicitly specified as unknown. if flippedRfc && flipped.Version() == 4 { cs += 1 } } return } /* Equal returns `true` if [uuid.UUID] the two provided [uuid.UUID] are the same. Currently it just wraps: eq = a == b but is provided as a safety guarantee if the underlying structures/types should change. */ func Equal(a, b uuid.UUID) (eq bool) { eq = a == b return } /* IsFlippedEndian can be used to check if [uuid.UUID] is a direct endian-flipped ([ToggleUuidMsGuid]) of b (or vice versa, obviously). It simply wraps: isFlipped = Equal(a, ToggleUuidMsGuid(b)) but can be useful for shorthand/readability. */ func IsFlippedEndian(a, b uuid.UUID) (isFlipped bool) { isFlipped = Equal(a, ToggleUuidMsGuid(b)) return } /* IsMaxUUID returns `true` if the specified UUID is explicitly an RFC-defined "Max UUID". (You may also see it specified in some places as the "Omni UUID".) For details, see RFC 9562 [§ 5.10]. [§ 5.10]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.10 */ func IsMaxUUID(u uuid.UUID) (isMax bool) { isMax = u == uuid.Max return } /* IsMsGuid wraps if cmp, _ = DetectMsGuid(msGUID, tgtVer); cmp < -3 { isMs = true } Note that [uuid.Microsoft] is an actual RFC-defined variant, but *Microsoft no longer uses it* and in MODERN implementations do the endianness flip [ToggleUuidMsGuid] of (USUALLY) a UUIDv4. See [DetectMsGuid] for a more in-depth result that will let you use the confidence level directly, and for details on the weird things that can go wrong with this guesswork. Note that this won't be 100% reliable due to math things, but it should be reliable enough most of the time. See also [MsGuidToUuid] and [UuidToMsGuid]. */ func IsMsGuid(msGUID uuid.UUID, tgtVer uuid.Version) (isMs bool) { var cmp int if cmp, _ = DetectMsGuid(msGUID, tgtVer); cmp < -3 { isMs = true } return } /* IsNcs is shorthand for: isNcs = u.Variant() == uuid.Reserved See also the notes in [IsRfc]. */ func IsNcs(u uuid.UUID) (isNcs bool) { // https://archive.org/details/networkcomputing0000zahn/page/10/mode/1up isNcs = u.Variant() == uuid.Reserved return } /* IsNilUUID returns `true` if the specified UUID is explicitly an RFC-defined "Nil UUID". For details, see RFC 9562 [§ 5.9]. [§ 5.9]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.9 */ func IsNilUUID(u uuid.UUID) (isNil bool) { isNil = u == uuid.Nil return } /* IsRfc returns `true` if the specified UUID is a proper standard RFC UUID. Because Microsoft is insane, rfc will be false even if it's a (legacy) Microsoft form of an RFC UUID. Use [IsMsGuid] for that. In the special case of u being a valid NCS UUID, rfc will be false but gen will be [Rfc4122]. This is because RFC 9652 deprecates the NCS UUID. See [IsNcs]. (You are highly unlikely to encounter an NCS UUID "in the wild" unless you are receiving a UUID from someone who severely misunderstands that UUIDs are structured/versioned/typed and thinks they're just random byes in hex with hyphens in certain places.) (They aren't that, if you're one of those someones.) Nil UUID ([IsNilUUID]) and Max UUID ([IsMaxUUID]) return true with RFCs 4122 and RFC 9562 respectively. */ func IsRfc(u uuid.UUID) (rfc bool, gen RfcGen) { if IsNilUUID(u) { rfc = true gen = Rfc4122 return } if IsMaxUUID(u) { rfc = true gen = Rfc9562 return } if IsNcs(u) { gen = Rfc4122 return } // TODO: Are there any sub-version checks that can be applied? switch u.Variant() { case uuid.Invalid, uuid.Microsoft, uuid.Future: return case uuid.RFC4122: if !(0x01 <= u.Version() && u.Version() <= 0x08) { return } rfc = true gen = Rfc4122 // 4122 only covers UUIDv1 through UUIDv5. if 0x06 <= u.Version() && u.Version() <= 0x08 { gen = Rfc9562 } default: // Safety net in case upstream adds a uuid.RFC9562 variant or something. if !(0x01 <= u.Version() && u.Version() <= 0x08) { return } if u.Variant() < uuid.Future { return } rfc = true gen = RfcNone // 4122 only covers UUIDv1 through UUIDv5. if 0x06 <= u.Version() && u.Version() <= 0x08 { gen = Rfc9562 } } return } /* IsValid indicates if the given [uuid.UUID] strictly conforms to RFC. A Nil UUID (as in RFC 9562 [§ 5.9], not a `nil` *uuid.UUID) will return `true` as it IS technically defined per RFC despite not conforming to a version. Use [IsNilUUID] to further determine that. Likewise, a Max UUID (RFC 9562 [§ 5.10]) will return `true` as it is also defined per RFC despite not conforming to a version. Use [IsMaxUUID] to further determine that. Microsoft GUIDs will always return false since they defy RFC. Use [IsMsGuid] to check for that condition. [§ 5.9]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.9 [§ 5.10]: https://datatracker.ietf.org/doc/html/rfc9562#section-5.10 */ func IsValid(u uuid.UUID) (valid bool) { if IsNilUUID(u) { valid = true return } if IsMaxUUID(u) { valid = true return } switch u.Variant() { case uuid.Invalid, uuid.Reserved, uuid.Microsoft, uuid.Future: return case uuid.RFC4122: valid = true // TODO: If they add an RFC9562 or something, need a case here. default: return } // If we got here, it *should* be RFC. if valid, _ = IsRfc(u); !valid { return } return } /* MsGuidToUuid converts a Microsoft GUID to a UUID. If [IsMsGuid] is false for msGUID, u will be equal to msGUID. See [UuidToMsGuid] for the inverse, and [IsRfc] to check if the result is a strictly conforming UUID. */ func MsGuidToUuid(msGUID uuid.UUID) (u uuid.UUID) { if !IsMsGuid(msGUID, 0x00) { u = msGUID return } u = ToggleUuidMsGuid(msGUID) return } /* ToggleUuidMsGuid switches the src to it's "other" format: * if it's a Microsoft GUID, it will be converted to a UUID * if it's a UUID, it will be converted to a Microsoft GUID No detection ([IsRfc], [IsMsGuid], etc.) nor validation/verification ([IsValid]) is performed, which is why this is a "toggle" - it just flips some endianness for certain byte ranges. If you prefer something a little more explicit, see [MsGuidToUuid] and/or [UuidToMsGuid]. Alternatively call [IsMsGuid] or [IsRfc] directly. */ func ToggleUuidMsGuid(orig uuid.UUID) (converted uuid.UUID) { var cb [16]byte var ob [16]byte = orig // Can just directly map the allocations; // the operation is the exact same regardless of whether the original is RFC and target is MS or vice versa. cb = [16]byte{ // THESE GET ENDIAN-SWAPPED ob[3], ob[2], ob[1], ob[0], // "A" ob[5], ob[4], // "B" ob[7], ob[6], // "C" // THESE STAY THE SAME (should be BE for both) ob[8], ob[9], ob[10], ob[11], // "D" ob[12], ob[13], ob[14], ob[15], // "E" } converted = uuid.UUID(cb) return } /* UuidToMsGuid converts a UUID to a Microsoft GUID. If [DetectMsGuid] indicates a good likelihood for u already being a Microsoft GUID (greater than or equal) to [MsGuidThreshold], msGUID will be equal to u. (If it detects it as unflipped endianness, it will automatically be flipped by this function.) See [MsGuidToUuid] for the inverse. */ func UuidToMsGuid(u uuid.UUID) (msGUID uuid.UUID) { var msCmp int var flipped int if msCmp, flipped = DetectMsGuid(u, 0x00); msCmp >= MsGuidThreshold && msCmp > flipped { msGUID = u return } msGUID = ToggleUuidMsGuid(u) return }