mirror of
https://github.com/qtmleap/revkit.git
synced 2026-09-28 14:01:50 +02:00
460 lines
17 KiB
Markdown
460 lines
17 KiB
Markdown
# ESN (Electronic Serial Number) — Android 生成アルゴリズム
|
|
|
|
> **対象:** Netflix Android v9.57.0 (build 63928)
|
|
> **デバイス:** Pixel 4a (5G) / bramble / Android 14
|
|
> **取得方法:** Frida ヒープダンプ (`hook_esn.js` → `Java.choose()`)
|
|
|
|
---
|
|
|
|
## 1. ESN の種類
|
|
|
|
Netflix Android では **4 種類の ESN** が使い分けられる。
|
|
|
|
| 種類 | メソッド | 用途 | 例 |
|
|
|---|---|---|---|
|
|
| **Base ESN (PRV)** | `b()` | デバイス登録・鍵交換 | `NFANDROID1-PRV-P-L3-GOOGLPIXEL=4A==5G=` |
|
|
| **Proxy ESN (PXA)** | `d()` | MSL 通信 (API コール) | `NFANDROID1-PXA-P-L3-GOOGLPIXEL=4A==5G=-22594-0202C...` |
|
|
| **Logblob ESN** | `j()` | テレメトリ・ログ | `NFANDROID1-GOOGLPIXEL=4A==5G=S-3269A5B5...` |
|
|
| **Migration Identity** | `getMigrationIdentity()` | アカウント移行 | `isWidevine=true:systemId=22594:deviceId=DCDC...:wveaVersion=2` |
|
|
|
|
---
|
|
|
|
## 2. ESN 構造
|
|
|
|
### 2.1 Base ESN (PRV)
|
|
|
|
```
|
|
NFANDROID1-PRV-P-L3-GOOGLPIXEL=4A==5G=
|
|
│ │ │ │ └─ sanitized(manufacturer_5 + model)
|
|
│ │ │ └─ Widevine Security Level (L1 or L3)
|
|
│ │ └─ Device Category (P=Phone, T=Tablet, B=TV, C=ChromeOS, E=Display)
|
|
│ └─ Type: PRV = Private (ローカル生成)
|
|
└─ Platform prefix (XOR 暗号化定数から復号)
|
|
```
|
|
|
|
### 2.2 Proxy ESN (PXA)
|
|
|
|
```
|
|
NFANDROID1-PXA-P-L3-GOOGLPIXEL=4A==5G=-22594-0202CPU0AVUCDE...
|
|
│ │ │ │ │ │ └─ サーバー発行 fingerprint
|
|
│ │ │ │ │ └─ Widevine systemId
|
|
│ │ │ │ └─ sanitized model (PRV と同じ)
|
|
│ │ │ └─ Security Level
|
|
│ │ └─ Device Category
|
|
│ └─ Type: PXA = Proxy (サーバー発行)
|
|
└─ Platform prefix
|
|
```
|
|
|
|
PXA ESN はサーバーから MSL レスポンスで受信し、`SharedPreferences` に永続化される。
|
|
|
|
### 2.3 Logblob ESN
|
|
|
|
```
|
|
NFANDROID1-GOOGLPIXEL=4A==5G=S-3269A5B59F399B066558C80DCB36AE97...
|
|
│ │ └─ HMAC-SHA256(android_id, hmacKey)
|
|
│ └─ logblob model (末尾に 'S' suffix)
|
|
└─ Platform prefix
|
|
```
|
|
|
|
### 2.4 Migration Identity
|
|
|
|
```
|
|
isWidevine=true:systemId=22594:deviceId=DCDCCAA5D63D1F75...:wveaVersion=2
|
|
│ └─ sanitize(hex(HMAC-SHA256(deviceUniqueId, hmacKey)))
|
|
└─ Widevine systemId
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 構築アルゴリズム
|
|
|
|
### 3.1 入力値
|
|
|
|
#### デバイス固有値
|
|
|
|
| 値 | 取得元 | 実測値 |
|
|
|---|---|---|
|
|
| `Build.MANUFACTURER` | `android.os.Build` | `Google` |
|
|
| `Build.MODEL` | `android.os.Build` | `Pixel 4a (5G)` |
|
|
| `android_id` | `Settings.Secure.getString()` | `ee6d59ac3434ec97` |
|
|
| Widevine `systemId` | `MediaDrm.getPropertyString("systemId")` | `22594` |
|
|
| Widevine `deviceUniqueId` | `MediaDrm.getPropertyByteArray("deviceUniqueId")` | `3c7f468d...` (32 bytes) |
|
|
| Widevine `securityLevel` | `MediaDrm.getPropertyString("securityLevel")` | `L3` (強制) |
|
|
|
|
#### XOR 暗号化定数 (`o.feO` / jadx: `C12892feO`)
|
|
|
|
`o.feO` クラスのコンストラクタで XOR 暗号 (`g()` メソッド) により復号される。
|
|
|
|
| フィールド | XOR seed | 文字数 | 復号値 | 用途 |
|
|
|---|---|---|---|---|
|
|
| `this.a` → `a()` | 263 | 11 | `NFANDROID1-` | Platform prefix |
|
|
| `this.d` → `e()` | 42043 | 14 | `20MNetflix2010` | HMAC-SHA256 秘密鍵 |
|
|
| `this.e` | 51941 | 52 | `amzn1.ask.skill.{uuid}` | Alexa skill ID |
|
|
| `this.b` → `b()` | 18671 | 124 | Base64 EC P-256 公開鍵 | 検証鍵 |
|
|
|
|
> **XOR 復号アルゴリズム** (`g()` メソッド):
|
|
> ```
|
|
> xor_base = -953881096168740883L ^ 7437109050103546004L
|
|
> for i in range(len(char_array)):
|
|
> long_val = (char_array[i] ^ (i * seed)) ^ xor_base
|
|
> result[i] = chr(long_val & 0xFFFF)
|
|
> ```
|
|
|
|
### 3.2 文字列サニタイズ
|
|
|
|
2 段階のサニタイズが適用される。
|
|
|
|
#### Step 1: `jNY.c(str)` — 空白→アンダースコア
|
|
|
|
```
|
|
"Pixel 4a (5G)" → "Pixel_4a_(5G)"
|
|
```
|
|
|
|
- `str.trim().replaceAll("\\s", "_")`
|
|
|
|
#### Step 2: `o.fkW.d(str)` (jadx: `C13218fkW.d`) — 大文字化 + 無効文字→`=`
|
|
|
|
```
|
|
"GooglPixel_4a_(5G)" → "GOOGLPIXEL=4A==5G="
|
|
```
|
|
|
|
- `str.toUpperCase(Locale.US)`
|
|
- `[^A-Z0-9\\-=]` にマッチする文字を `=` に置換
|
|
|
|
#### 実例
|
|
|
|
```
|
|
Build.MANUFACTURER = "Google"
|
|
Build.MODEL = "Pixel 4a (5G)"
|
|
|
|
manufacturer_5 = "Google"[:5] = "Googl"
|
|
model_sanitized = jNY.c("Pixel 4a (5G)") = "Pixel_4a_(5G)"
|
|
combined = "Googl" + "Pixel_4a_(5G)" = "GooglPixel_4a_(5G)"
|
|
esn_model = fkW.d("GooglPixel_4a_(5G)") = "GOOGLPIXEL=4A==5G="
|
|
```
|
|
|
|
### 3.3 Base ESN 構築 (`b()`)
|
|
|
|
```java
|
|
// WidevineEntityAuthEsnProviderImpl.b()
|
|
String prefix = i(); // "NFANDROID1-PRV-P-L3-"
|
|
String model = Build.MODEL; // "Pixel 4a (5G)"
|
|
if (model.length() > 45) model = model.substring(0, 45);
|
|
String mfr = deviceModelProvider.e(); // "Googl" (first 5 chars)
|
|
String sanitizedModel = jNY.c(model); // "Pixel_4a_(5G)"
|
|
String combined = mfr + sanitizedModel; // "GooglPixel_4a_(5G)"
|
|
return prefix + fkW.d(combined); // "NFANDROID1-PRV-P-L3-GOOGLPIXEL=4A==5G="
|
|
```
|
|
|
|
### 3.4 Prefix 構築 (`i()`)
|
|
|
|
```java
|
|
// WidevineEntityAuthEsnProviderImpl.i()
|
|
StringBuilder sb = new StringBuilder(esnPrefixConfig.a()); // "NFANDROID1-"
|
|
sb.append("PRV-");
|
|
|
|
switch (deviceCategory) {
|
|
case PHONE: sb.append("P-"); break;
|
|
case TABLET: sb.append("T-"); break;
|
|
case CHROMEOS: sb.append("C-"); break;
|
|
case TV:
|
|
case STB: sb.append("B-"); break;
|
|
case DISPLAY: sb.append("E-"); break;
|
|
}
|
|
|
|
if (cryptoProvider == WIDEVINE_L3) {
|
|
sb.append("L3-");
|
|
}
|
|
|
|
// 6番目の '-' までで切り詰め
|
|
return sb.toString().substring(0, indexOf6thDash + 1);
|
|
// → "NFANDROID1-PRV-P-L3-"
|
|
```
|
|
|
|
### 3.5 Migration Identity 構築
|
|
|
|
```java
|
|
// WidevineEntityAuthEsnProviderImpl.getMigrationIdentity()
|
|
String systemId = widevineSupport.b(); // "22594"
|
|
byte[] deviceUid = widevineSupport.d(); // 32 bytes
|
|
String hmacKey = esnPrefixConfig.e(); // "20MNetflix2010"
|
|
|
|
Mac mac = Mac.getInstance("HmacSHA256");
|
|
mac.init(new SecretKeySpec(hmacKey.getBytes(UTF_8), "HmacSHA256"));
|
|
byte[] hmacResult = mac.doFinal(deviceUid);
|
|
String hexHash = toUpperHex(hmacResult); // uppercase hex (64 chars)
|
|
String sanitized = fkW.d(hexHash); // already valid, no change
|
|
|
|
return "isWidevine=true:systemId=" + systemId
|
|
+ ":deviceId=" + sanitized
|
|
+ ":wveaVersion=2";
|
|
```
|
|
|
|
### 3.6 Logblob ESN 構築 (`j()`)
|
|
|
|
```java
|
|
// WidevineEntityAuthEsnProviderImpl.j()
|
|
String platform = esnPrefixConfig.a(); // "NFANDROID1-"
|
|
String logModel = deviceModelProvider.a(); // "GOOGLPIXEL=4A==5G=S"
|
|
String hashId = deviceModelProvider.c(); // HMAC-SHA256(android_id, hmacKey)
|
|
return platform + logModel + "-" + hashId;
|
|
```
|
|
|
|
---
|
|
|
|
## 4. HMAC 計算の詳細
|
|
|
|
### 4.1 Migration Identity 用 (deviceUniqueId ベース)
|
|
|
|
```
|
|
Key: "20MNetflix2010" (UTF-8 bytes: 32304d4e6574666c697832303130)
|
|
Input: deviceUniqueId bytes (32 bytes from MediaDrm)
|
|
3c7f468d8a9a78c0609acd65b0660b61a1178ec9978d9e4bcb71e53ff190d974
|
|
|
|
HMAC-SHA256 → DCDCCAA5D63D1F757C7B1D16C1D3AAA873112C30C1448E8E0510D78C9AA17DD4
|
|
```
|
|
|
|
### 4.2 Logblob ESN 用 (android_id ベース)
|
|
|
|
```
|
|
Key: "20MNetflix2010" (UTF-8 bytes)
|
|
Input: android_id string as UTF-8 bytes
|
|
"ee6d59ac3434ec97" → 65653664353961633334333465633937
|
|
|
|
HMAC-SHA256 → 3269A5B59F399B066558C80DCB36AE97F9A8B477F8854DFED2C14C8453D6F556
|
|
```
|
|
|
|
> **注意:** `deviceUniqueId` はバイナリ (MediaDrm 直接取得)、
|
|
> `android_id` は文字列の UTF-8 バイト列として HMAC に渡される。
|
|
|
|
---
|
|
|
|
## 5. 永続化 (SharedPreferences)
|
|
|
|
| キー | 値 |
|
|
|---|---|
|
|
| `nf_drm_esn` | PXA ESN (文字列) |
|
|
| `nf_drm_proxy_esn` | JSON: `{"esn": "...", "ts": epoch_ms, "sn": serial_number}` |
|
|
| `nf_drm_migration_identity` | Migration Identity 文字列 |
|
|
|
|
---
|
|
|
|
## 6. クラスマッピング
|
|
|
|
| 実行時クラス名 | jadx リネーム名 | 役割 |
|
|
|---|---|---|
|
|
| `o.feO` | `C12892feO` | EsnPrefixConfig — XOR 暗号化定数 |
|
|
| `o.fkW` | `C13218fkW` | EsnProviderUtils — 文字列サニタイズ |
|
|
| `o.fkn` | `C13235fkn` | DeviceModelProvider — モデル文字列加工 |
|
|
| `o.fey` | `InterfaceC12928fey` | EsnPrefixConfig interface |
|
|
| `o.jNK` | — | SharedPreferences ヘルパー |
|
|
| `o.jNY` | — | 文字列ユーティリティ (空白→`_`) |
|
|
| `o.jMn` | `C20944jMn` | Hex エンコーダ (uppercase) |
|
|
| `o.fkX` | `C13219fkX` | MigrationIdentity 文字列ビルダー |
|
|
| — | `WidevineEntityAuthEsnProviderImpl` | ESN 組み立て本体 |
|
|
| — | `WidevineSupportImpl` | Widevine DRM メタデータ |
|
|
| — | `DeviceIdentityUtils` | android_id → HMAC fingerprint |
|
|
| — | `ProxyEsn` | PXA ESN 管理 |
|
|
|
|
---
|
|
|
|
## 7. プラットフォームプレフィックス一覧
|
|
|
|
今回のリバースエンジニアリング対象(Android APK + iOS IPA)から判明したプレフィックスは **3 種類のみ**。
|
|
他プラットフォーム (Chromecast, Roku, Smart TV, PlayStation, Xbox 等) のプレフィックスは本調査範囲では発見されておらず、存在するかどうかも不明。
|
|
|
|
### 7.1 プラットフォームプレフィックス
|
|
|
|
| プレフィックス | プラットフォーム | 発見元 | 備考 |
|
|
|---|---|---|---|
|
|
| `NFANDROID1-` | Android (Google Play) | `o.feO` の XOR 定数 (seed=263) | 主要 Android 向け |
|
|
| `NFANDROIDD-` | Android (非 GMS / デバッグ) | `jNG.java`, `CryptoProviderFactoryImpl.java` | L3 チェック条件にのみ出現 |
|
|
| `NFAPPL-` | iOS (Apple) | ログ / MSL トラフィック / `docs/msl_ios.md` | FairPlay ベース |
|
|
|
|
> **`NFANDROIDD-` について:**
|
|
> `jNG.c()` と `CryptoProviderFactoryImpl.shouldKeepDeviceOnWidevineL3()` で
|
|
> `NFANDROID1-PRV-S-L3-` と並列でチェックされている。
|
|
> `S` は特殊なプロビジョニング状態を示すと推測される(通常のトラフィックでは `P-` / `T-` 等のみ確認)。
|
|
|
|
### 7.2 デバイスカテゴリコード
|
|
|
|
`DeviceCategory` enum から ESN の第 3 セグメントへのマッピング:
|
|
|
|
| enum 値 | 文字列表現 | ESN コード | 備考 |
|
|
|---|---|---|---|
|
|
| `UNKNOWN` | `"unknown"` | *(なし)* | switch 文にマッチなし → コード付与されない |
|
|
| `PHONE` | `"phone"` | `P-` | |
|
|
| `TABLET` | `"tablet"` | `T-` | |
|
|
| `GOOGLE_TV` | `"google-tv"` | *(なし)* | switch 文にマッチなし |
|
|
| `ANDROID_TV` | — | `B-` | Box 系として統合 |
|
|
| `CHROME_OS` | — | `C-` | |
|
|
| `ANDROID_STB` | — | `B-` | ANDROID_TV と同じ `B-` |
|
|
| `SMART_DISPLAY` | `"smart-display"` | `E-` | |
|
|
|
|
> **検出優先順位** (高→低): `CHROME_OS` → `ANDROID_STB` → `ANDROID_TV` → `TABLET` → `PHONE` (フォールバック)
|
|
|
|
### 7.3 セキュリティレベルサフィックス
|
|
|
|
`CryptoProvider` enum から ESN prefix 末尾へのマッピング:
|
|
|
|
| enum 値 | ESN サフィックス | NCCP 値 | 備考 |
|
|
|---|---|---|---|
|
|
| `WIDEVINE_L1` | *(なし)* | 1 | L3 マーカーなし = L1 を暗示 |
|
|
| `WIDEVINE_L3` | `L3-` | 3 | 明示的に `L3-` が付与される |
|
|
| `NONE` | *(なし)* | 0 | |
|
|
| `OEM_CRYPTO` | *(なし)* | 4 | |
|
|
| `NATIVE` | *(なし)* | 5 | |
|
|
|
|
### 7.4 プラットフォーム間の ESN 構造比較
|
|
|
|
| 属性 | Android | iOS |
|
|
|---|---|---|
|
|
| **プレフィックス** | `NFANDROID1-` / `NFANDROIDD-` | `NFAPPL-` |
|
|
| **DRM バージョン** | なし | `02` (FairPlay v2) |
|
|
| **ESN タイプ** | `PRV-` / `PXA-` (第 2 セグメント) | Base にはなし; PXA はモデル後に `-PXA-` |
|
|
| **デバイスカテゴリ** | `P-`/`T-`/`B-`/`C-`/`E-` (第 3 セグメント) | なし |
|
|
| **セキュリティレベル** | `L3-` or 省略 (=L1) | なし (FairPlay に L1/L3 区別なし) |
|
|
| **モデルエンコード** | `GOOGLPIXEL=4A==5G=` (mfr5 + model) | `IPHONE9=1` (hw model, `,`→`=`) |
|
|
| **システム ID** | Widevine `systemId` (PXA に付与) | なし |
|
|
| **ハッシュ/fingerprint** | 64 文字 hex (HMAC-SHA256) | 64 文字 hex (SHA-256) |
|
|
|
|
### 7.5 全 ESN パターン一覧
|
|
|
|
```
|
|
== Android ==
|
|
Base (L1): NFANDROID1-PRV-P-{MODEL}
|
|
Base (L3): NFANDROID1-PRV-P-L3-{MODEL}
|
|
Proxy (L3): NFANDROID1-PXA-P-L3-{MODEL}-{systemId}-{fingerprint}
|
|
Logblob: NFANDROID1-{MODEL}S-{hmac_hash}
|
|
Migration: isWidevine=true:systemId={id}:deviceId={hash}:wveaVersion=2
|
|
Debug L3 Check: NFANDROIDD-PRV-S-L3-...
|
|
|
|
== iOS ==
|
|
Base: NFAPPL-02-{MODEL}-{hash}
|
|
Proxy (PXA): NFAPPL-02-{MODEL}-PXA-{fingerprint}
|
|
device_type: NFAPPL-02-
|
|
```
|
|
|
|
---
|
|
|
|
## 8. セキュリティ上の所見
|
|
|
|
1. **HMAC 秘密鍵がハードコード:** `20MNetflix2010` は XOR 難読化されているのみで、静的解析で復元可能
|
|
2. **XOR 暗号は脆弱:** 既知平文攻撃に対して脆弱。`g()` メソッドのシードと XOR base が判明すれば全定数を復号可能
|
|
3. **ESN は再生成可能:** `android_id`、`Build.*` プロパティ、Widevine DRM 情報があれば ESN を再構築できる
|
|
4. **L3 強制:** ハードウェアが L1 対応でも `CryptoProvider.WIDEVINE_L3` でアプリは L3 モードで動作する
|
|
5. **PXA ESN の fingerprint:** サーバー発行であり、ローカルでは再現不可。`ProxyEsnMslRequest` で MSL 経由取得
|
|
|
|
---
|
|
|
|
## 9. ESN 生成フロー図
|
|
|
|
### 9.1 Base ESN (PRV) — ローカル生成
|
|
|
|
デバイス登録・鍵交換に使用される基本 ESN。全てローカルで生成される。
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
subgraph inputs["入力データ"]
|
|
config["<b>EsnPrefixConfig</b> (XOR 復号)<br/>platformPrefix = 'NFANDROID1-'"]
|
|
build_mfr["Build.MANUFACTURER<br/>'Google'"]
|
|
build_model["Build.MODEL<br/>'Pixel 4a (5G)'"]
|
|
cat["DeviceCategory<br/>PHONE → 'P-'"]
|
|
level["CryptoProvider<br/>WIDEVINE_L3 → 'L3-'"]
|
|
end
|
|
|
|
config --> prefix_build["Prefix 構築"]
|
|
cat --> prefix_build
|
|
level --> prefix_build
|
|
prefix_build --> prefix["'NFANDROID1-PRV-P-L3-'"]
|
|
|
|
build_mfr --> mfr5["先頭 5 文字<br/>'Googl'"]
|
|
build_model --> trim["45 文字で切り詰め"]
|
|
trim --> spaces["空白 → '_'<br/>'Pixel_4a_(5G)'"]
|
|
mfr5 --> combine["結合<br/>'GooglPixel_4a_(5G)'"]
|
|
spaces --> combine
|
|
combine --> sanitize["大文字化 + 無効文字→'='<br/>'GOOGLPIXEL=4A==5G='"]
|
|
|
|
prefix --> result(["<b>Base ESN (PRV)</b><br/>NFANDROID1-PRV-P-L3-GOOGLPIXEL=4A==5G="])
|
|
sanitize --> result
|
|
|
|
style inputs fill:#1a1a2e,stroke:#e94560,color:#eee
|
|
style result fill:#0f3460,stroke:#e94560,color:#eee
|
|
```
|
|
|
|
### 9.2 Proxy ESN (PXA) — サーバー発行
|
|
|
|
MSL 通信 (API コール) で使用される ESN。サーバーが systemId と fingerprint を付与して発行する。
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
base_esn["Base ESN (PRV)<br/>NFANDROID1-PRV-P-L3-GOOGLPIXEL=4A==5G="]
|
|
|
|
base_esn -->|"ProxyEsnMslRequest<br/>MSL リクエスト"| server["<b>Netflix Server</b>"]
|
|
server -->|"PXA ESN 発行"| result(["<b>Proxy ESN (PXA)</b><br/>NFANDROID1-PXA-P-L3-GOOGLPIXEL=4A==5G=-22594-0202CPU0AVUCDE..."])
|
|
|
|
result --> prefs["SharedPreferences 永続化<br/>nf_drm_esn = PXA ESN<br/>nf_drm_proxy_esn = JSON{esn, ts, sn}"]
|
|
|
|
note["PXA の追加フィールド:<br/>• systemId (Widevine) = 22594<br/>• fingerprint = サーバー発行 (ローカル再現不可)"]
|
|
|
|
style server fill:#533483,stroke:#e94560,color:#eee
|
|
style result fill:#0f3460,stroke:#e94560,color:#eee
|
|
style prefs fill:#16213e,stroke:#0f3460,color:#eee
|
|
style note fill:#1a1a2e,stroke:#555,color:#aaa
|
|
```
|
|
|
|
### 9.3 Logblob ESN — テレメトリ用
|
|
|
|
ログ送信時に使用される ESN。android_id から HMAC で生成。
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
subgraph inputs["入力データ"]
|
|
config["<b>EsnPrefixConfig</b> (XOR 復号)<br/>platformPrefix = 'NFANDROID1-'<br/>hmacSecretKey = '20MNetflix2010'"]
|
|
aid["android_id<br/>'ee6d59ac3434ec97'"]
|
|
model_prov["DeviceModelProvider<br/>logblob model = 'GOOGLPIXEL=4A==5G=S'"]
|
|
end
|
|
|
|
config -->|"hmacSecretKey"| hmac["HMAC-SHA256<br/>key = '20MNetflix2010'<br/>data = android_id (UTF-8 bytes)"]
|
|
aid --> hmac
|
|
hmac --> hash["ハッシュ (64 文字 hex)<br/>'3269A5B59F399B06...'"]
|
|
|
|
config -->|"platformPrefix"| concat["文字列結合"]
|
|
model_prov --> concat
|
|
hash --> concat
|
|
|
|
concat --> result(["<b>Logblob ESN</b><br/>NFANDROID1-GOOGLPIXEL=4A==5G=S-3269A5B59F399B06..."])
|
|
|
|
style inputs fill:#1a1a2e,stroke:#e94560,color:#eee
|
|
style result fill:#0f3460,stroke:#e94560,color:#eee
|
|
```
|
|
|
|
### 9.4 Migration Identity — アカウント移行用
|
|
|
|
デバイス間のアカウント移行に使用される識別子。ESN 形式ではなくキーバリュー形式。
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
subgraph inputs["入力データ"]
|
|
config["<b>EsnPrefixConfig</b> (XOR 復号)<br/>hmacSecretKey = '20MNetflix2010'"]
|
|
drm_sid["Widevine systemId<br/>'22594'"]
|
|
drm_uid["Widevine deviceUniqueId<br/>(32 bytes: 3c7f468d...)"]
|
|
end
|
|
|
|
config -->|"hmacSecretKey"| hmac["HMAC-SHA256<br/>key = '20MNetflix2010'<br/>data = deviceUniqueId (raw bytes)"]
|
|
drm_uid --> hmac
|
|
hmac --> hash["uppercase hex (64 文字)<br/>'DCDCCAA5D63D1F75...'"]
|
|
hash --> sanitize["サニタイズ (変化なし)"]
|
|
|
|
drm_sid --> build["文字列構築"]
|
|
sanitize --> build
|
|
|
|
build --> result(["<b>Migration Identity</b><br/>isWidevine=true:systemId=22594:deviceId=DCDCCAA5D63D1F75...:wveaVersion=2"])
|
|
|
|
result --> prefs["SharedPreferences 永続化<br/>nf_drm_migration_identity"]
|
|
|
|
style inputs fill:#1a1a2e,stroke:#e94560,color:#eee
|
|
style result fill:#0f3460,stroke:#e94560,color:#eee
|
|
style prefs fill:#16213e,stroke:#0f3460,color:#eee
|
|
```
|