While digging through the assembly code of the Android Widevine library libwvhidl.so (API Level 26, Android 8.0) and also libwvdrmengine.so (API Level 32, Android 12.0), I stumbled upon something strange: a UUID that had no business being there.
More recent versions are stripped and have changed a little, but I found a similar reference in sub_50F54 inside android.hardware.drm-service.widevine (API Level 36, Android 15.0) when searching for the byte sequence EA 83 BC F2:
bool __fastcall sub_50F54(_QWORD *a1)
{
if ( *a1 == 0xCE4AD679A98BEFEDLL && a1[1] == 0xED211DD5DC27C8A3LL )
return 1;
return *a1 == 0x344AC73CE41F7029LL && a1[1] == 0x479A43C790AE5B8CLL;
}
## Discovery of the suspicious code
Widevine’s `isCryptoSchemeSupported` function contained this interesting check:
```cpp
bool __fastcall wvdrm::isWidevineUUID(wvdrm *this, const unsigned __int8 *a2)
{
return !(*(_QWORD *)this ^ 0xCE4AD679A98BEFEDLL | *((_QWORD *)this + 1) ^ 0xED211DD5DC27C8A3LL)
|| (*(_QWORD *)this ^ 0x344AC73CE41F7029LL | *((_QWORD *)this + 1) ^ 0x479A43C790AE5B8CLL) == 0;
}
Two UUIDs were hardcoded directly in the binary. The first one I recognized immediately — clearly Widevine. But the second? No idea.
The logic was simple: if the UUID passed in parameter matches one of the two values, the function returns true. Otherwise, false. Nothing exceptional — but why two UUIDs?
Decoding the UUIDs from their memory representation
Decoding UUIDs from their little-endian representation was annoying. The UUID specification is clear about the format, but when you’re dealing with raw 64-bit memory values, you need to rebuild the fields in the right order.
def uuid_from_little_endian_bytes(low64, high64):
# Convert 64-bit values to bytes (little-endian)
bytes_low = low64.to_bytes(8, 'little')
bytes_high = high64.to_bytes(8, 'little')
# Reconstruct UUID fields
# (note: the first 3 fields are big-endian in the final UUID format)
time_low = int.from_bytes(bytes_low[0:4], 'big')
time_mid = int.from_bytes(bytes_low[4:6], 'big')
time_hi = int.from_bytes(bytes_low[6:8], 'big')
clock_seq = int.from_bytes(bytes_high[0:2], 'big')
node = int.from_bytes(bytes_high[2:8], 'big')
return f"{time_low:08x}-{time_mid:04x}-{time_hi:04x}-{clock_seq:04x}-{node:012x}"
# Decode both UUIDs found
print(uuid_from_little_endian_bytes(0xCE4AD679A98BEFED, 0xED211DD5DC27C8A3))
# Result: edef8ba9-79d6-4ace-a3c8-27dcd51d21ed (official Widevine UUID)
print(uuid_from_little_endian_bytes(0x344AC73CE41F7029, 0x479A43C790AE5B8CLL))
# Result: 29701fe4-3cc7-4a34-8c5b-ae90c7439a47 (mysterious UUID)
The first UUID matches the official Widevine identifier according to DASHIF. But the second one appears nowhere in official documentation.
Investigation of the mysterious UUID
I dug through archives and forums to understand where this 29701fe4-3cc7-4a34-8c5b-ae90c7439a47 came from. The history is:
2015 — First mentioned on the Apple Developer Forums, suspected to be related to early FairPlay 2.0.
2017 — An official issue on DASH-IF proposes replacing it with the official FairPlay UUID 94ce86fb-07ff-4f43-adb8-93d2fa968ca2.
2018 — A commit in Shaka Packager adds support for this UUID for FairPlay.
2023 — Shaka Packager migrates to the official UUID, but keeps the old one “for backwards compatibility only.”
2025 — A pastebin claims Netflix still uses this unofficial UUID for FairPlay.
In short, this UUID seems to have been an early, unofficial FairPlay identifier used before standardization.
2015 : Première mention sur les forums Apple Developer, soupçonné d’être lié à FairPlay 2.0.
2017 : Une issue officielle sur DASH-IF propose de le remplacer par l’UUID FairPlay officiel 94ce86fb-07ff-4f43-adb8-93d2fa968ca2.
2018 : Commit dans Shaka Packager qui ajoute le support de cet UUID pour FairPlay.
2023 : Shaka Packager migre vers l’UUID officiel, mais garde l’ancien “for backwards compatibility only”.
2025 : Un pastebin mentionne que Netflix utiliserait encore cet UUID non-officiel pour FairPlay.
What I eventually concluded
Widevine’s isCryptoSchemeSupported function accepts two UUIDs:
- The official Widevine UUID (
edef8ba9-79d6-4ace-a3c8-27dcd51d21ed) - An old, unofficial FairPlay UUID (
29701fe4-3cc7-4a34-8c5b-ae90c7439a47)
long double __usercall wvdrm::hardware::drm::V1_4::widevine::WVCryptoFactory::isCryptoSchemeSupported@<Q0>(
wvdrm *this@<X1>,
__int64 a2@<X8>)
{
bool isWidevineUUID;
long double result;
// Check whether the UUID matches Widevine OR the old FairPlay ID
isWidevineUUID = wvdrm::isWidevineUUID(this, (const unsigned __int8 *)this);
// Initialize output buffer and store result
*(_OWORD *)&result = 0u;
*(_BYTE *)(a2 + 32) = 0;
*(_OWORD *)a2 = 0u;
*(_OWORD *)(a2 + 16) = 0u;
*(_BYTE *)(a2 + 33) = isWidevineUUID; // The actual result
return result;
}
The function merely sets a flag in a buffer (offset 33) and always returns 0. It’s not strict validation — more like an informational detection for upper layers.
Which brings me to the question:
Why does this UUID 29701fe4-3cc7-4a34-8c5b-ae90c7439a47, historically tied to unofficial FairPlay, appear inside an Android library related to Widevine?
Netflix APK
2025-11-15 Update: After analyzing an Netflix APK with jadx, I confirmed that it still contains the 29701fe4-3cc7-4a34-8c5b-ae90c7439a47 UUID within its Widevine-related libraries.

The UUID built from those two long values is:
static final UUID NetflixWidevineUUID =
new UUID(2985921618079337012L, -8332874748677350841L);
Using the Java UUID constructor (mostSigBits, leastSigBits), the resulting UUID is: 29701fe4-3cc7-4a34-8c5b-ae90c7439a47!
I still don’t know why Netflix would include this old FairPlay UUID in their Widevine code, but it’s definitely there. Mystery remains unsolved.