stremio/apps/ios/VMC/Playback/ClearKeyMaterial.swift
Jos Vooges | STH fac38d905e Enable iOS ClearKey DASH playback via local CENC decrypt proxy.
MobileVLCKit cannot decrypt ClearKey; the app now decrypts segments in-process and feeds plain DASH to VLC, removing the coming-soon DRM dead-end (0.5.1/13).
2026-09-19 06:37:11 +02:00

70 lines
2.6 KiB
Swift

import Foundation
/// ClearKey kids/keys zoals Android `buildClearKeyLicenseJson` (hex 32 + GUID-endianness).
struct ClearKeyKeyMap {
/// kid-hex (lowercase, 32 chars) → key bytes (16)
private let keysByKid: [String: Data]
init(entries: [ClearKeyEntry]) {
var map: [String: Data] = [:]
for entry in entries {
guard let kid = Self.hex16(entry.kid), let key = Self.hex16(entry.key) else { continue }
let kidHex = kid.map { String(format: "%02x", $0) }.joined()
map[kidHex] = key
let swapped = Self.swapUuidEndianness(kid)
let swappedHex = swapped.map { String(format: "%02x", $0) }.joined()
if swappedHex != kidHex {
map[swappedHex] = key
}
}
keysByKid = map
}
var isEmpty: Bool { keysByKid.isEmpty }
/// Eerste key (streams hebben meestal één kid/key-paar).
var primaryKey: Data? { keysByKid.values.first }
func key(forKid kid: Data) -> Data? {
guard kid.count == 16 else { return primaryKey }
let hex = kid.map { String(format: "%02x", $0) }.joined()
if let k = keysByKid[hex] { return k }
let swapped = Self.swapUuidEndianness(kid)
let swappedHex = swapped.map { String(format: "%02x", $0) }.joined()
return keysByKid[swappedHex] ?? primaryKey
}
static func sanitizeHex(_ raw: String) -> String {
raw.trimmingCharacters(in: .whitespacesAndNewlines)
.replacingOccurrences(of: "0x", with: "", options: .caseInsensitive)
.replacingOccurrences(of: "-", with: "")
.replacingOccurrences(of: ":", with: "")
.replacingOccurrences(of: " ", with: "")
.lowercased()
}
static func hex16(_ raw: String) -> Data? {
let clean = sanitizeHex(raw)
guard clean.count == 32, clean.allSatisfy({ $0.isHexDigit }) else { return nil }
var out = Data(capacity: 16)
var i = clean.startIndex
while i < clean.endIndex {
let j = clean.index(i, offsetBy: 2)
guard let b = UInt8(clean[i..<j], radix: 16) else { return nil }
out.append(b)
i = j
}
return out
}
/// Microsoft GUID / sommige ClearKey packs: DWORD/WORD little-endian in eerste 8 bytes.
static func swapUuidEndianness(_ b: Data) -> Data {
guard b.count == 16 else { return b }
return Data([
b[3], b[2], b[1], b[0],
b[5], b[4],
b[7], b[6],
b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15],
])
}
}