# MIB Faisanet — Login Flow Fully reverse engineered from a captured HAR trace of a first-time device registration followed immediately by a regular login. --- ## Key Corrections The DH parameter names in the app's source are **misleading**: | App variable | DH role | Value | |---|---|---| | `A_VALUE` | Exponent / client private key | `15635168026...` (shorter) | | `P_VALUE` | Prime modulus | `24103124269...` (longer) | | `G_VALUE` | Generator | `2` | The session key derivation is: ```python shared = pow(smod, A_VALUE, P_VALUE) # NOT pow(smod, P_VALUE, A_VALUE) sha256_hex = SHA256(str(shared)).upper() session_key = base64(bytes.fromhex(sha256_hex)) ``` --- ## Overview The full login sequence consists of two phases: | Phase | Purpose | Key used | |---|---|---| | **Phase 1** — Device registration | First time this device+account pair is seen | DH session key from `sfunc=r` | | **Phase 2** — Regular login | Every subsequent login | key1/key2 (from phase 1) → second DH → new session key | --- ## Full Flow Diagram ``` Client Server | | | [0] sfunc=r (DEFAULT_KEY) | | { cmod, appId, routePath:S40, ... } | |--------------------------------------------->| | { smod, nonceGenerator, xxid, ... } | |<---------------------------------------------| | derive session_key_1 = DH(smod) | | | | [1] sfunc=n routePath:A44 (session_key_1)| | { uname } | |--------------------------------------------->| | { loginType, userSalt } | |<---------------------------------------------| | | | [2] sfunc=n routePath:C41 (session_key_1)| ← device registration init | { uname, pgf03, clientSalt } | |--------------------------------------------->| | { key1, key2, otpTypes, fullName, ... } | |<---------------------------------------------| | | | [3] sfunc=n routePath:C42 (session_key_1)| ← OTP verify (registration) | { otp, uname, otpType } | |--------------------------------------------->| | { key1, key2, encryptionMethod:2, ... } | |<---------------------------------------------| | store key1, key2 on device | | | | [4] sfunc=i (key1) | ← second DH key exchange | { cmod, appId, routePath:S40, key2 } | |--------------------------------------------->| | { smod, nonceGenerator, xxid, ... } | |<---------------------------------------------| | derive session_key_2 = DH(smod) | | | | [5] sfunc=n routePath:A44 (session_key_2)| | { uname } | |--------------------------------------------->| | { loginType, userSalt } | |<---------------------------------------------| | | | [6] sfunc=n routePath:A41 (session_key_2)| ← regular login init | { uname, pgf03, clientSalt, requireBankData:1 }| |--------------------------------------------->| | { primaryOTPType, otpTypes, email, uuid, uuid2, ... }| |<---------------------------------------------| | | | [7] sfunc=n routePath:P41 (session_key_2)| ← fetch profile image | { imageHash } | |--------------------------------------------->| | { profileImage (base64 JPEG) } | |<---------------------------------------------| | | | [8] sfunc=n routePath:A42 (session_key_2)| ← OTP verify (regular login) | { otp, uname, otpType } | |--------------------------------------------->| | { ... session established ... } | |<---------------------------------------------| ``` --- ## Step-by-Step Reference ### [0] Initial Key Exchange — `sfunc=r` **Key**: `DEFAULT_KEY = 8M3L9SBF1AC4FRE56788M3L9SBF1AC4FRE5678` **Request body** (inner `data` field): ```json { "cmod": "", "appId": "IOS17.2-<15 random chars>", "routePath": "S40", "sodium": "", "xxid": "" } ``` **Full request** (outer wrapper, encrypted together): ```json { "sfunc": "r", "data": { ...above... } } ``` **Response** (decrypted with DEFAULT_KEY): ```json { "success": true, "reasonCode": "201", "reasonText": "Key generated successfully.", "smod": "", "nonceGenerator": "", "xxid": "", "sodium": "", "encMethod": 2 } ``` After this step: - Derive `session_key_1 = derive_session_key(smod)` - Save `xxid` and `nonceGenerator` --- ### [1] Get Auth Type — `sfunc=n`, `routePath: A44` **Key**: `session_key_1` **Request** (encrypted): ```json { "sfunc": "n", "xxid": "", "data": { "uname": "", "nonce": "", "appId": "", "sodium": "", "routePath": "A44", "xxid": "" } } ``` **Response**: ```json { "success": true, "reasonCode": "108", "reasonText": "Auth type retrieved!", "data": [ { "loginType": "1", "userSalt": "" } ] } ``` --- ### [2] Device Registration Init — `sfunc=n`, `routePath: C41` First-time only. Registers this device+account pair. **Key**: `session_key_1` **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "uname": "", "pgf03": "", "clientSalt": "", "nonce": "", "appId": "", "sodium": "", "routePath": "C41", "xxid": "" } } ``` **Response**: ```json { "success": true, "reasonCode": "201", "reasonText": "Registration Initialization Successfully.", "primaryOTPType": "3", "roleName": "Consumer Premium", "otpTypes": [2, 3], "fullName": "", "lastLoginTime": "", "customerImgHash": "" } ``` --- ### [3] OTP Verification (Registration) — `sfunc=n`, `routePath: C42` **Key**: `session_key_1` **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "otp": "<6-digit OTP>", "uname": "", "otpType": "3", "nonce": "", "appId": "", "sodium": "", "routePath": "C42", "xxid": "" } } ``` **Response**: ```json { "success": true, "reasonCode": "101", "reasonText": "registration success", "data": [ { "appId": "", "createdDate": "", "key1": "", "key2": "", "encryptionMethod": "2", "appAgent": "android/1.0" } ] } ``` `key1` and `key2` are long-lived device credentials. `key1` is the Blowfish key for the next `sfunc=i` call. `key2` is sent as plaintext in the outer wrapper of that call. --- ### [4] Authenticated Key Exchange — `sfunc=i` Second DH exchange, authenticated with the device credentials. **Key**: `key1` **Request** (outer wrapper includes `key2`): ```json { "sfunc": "i", "key2": "", "data": { "cmod": "", "appId": "", "routePath": "S40", "sodium": "", "xxid": "" } } ``` **Response** (decrypted with `key1`): ```json { "success": true, "reasonCode": "201", "reasonText": "Key generated successfully.", "smod": "", "nonceGenerator": "", "xxid": "", "encMethod": 2 } ``` After this step: - Derive `session_key_2 = derive_session_key(smod)` - Replace `xxid` and `nonceGenerator` with new values --- ### [5] Get Auth Type — `sfunc=n`, `routePath: A44` Same as step [1] but with `session_key_2`. Fetches `userSalt` for password hashing. --- ### [6] Regular Login Init — `sfunc=n`, `routePath: A41` **Key**: `session_key_2` **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "uname": "", "pgf03": "", "clientSalt": "", "pmodTime": 0, "requireBankData": 1, "nonce": "", "appId": "", "sodium": "", "routePath": "A41", "xxid": "" } } ``` **Response**: ```json { "success": true, "reasonCode": "104", "reasonText": "Initialization Successful", "primaryOTPType": "3", "roleName": "Consumer Premium", "otpTypes": [2, 3], "email": "", "uuid": "", "uuid2": "", "xxid": "" } ``` --- ### [7] Get Profile Image — `sfunc=n`, `routePath: P41` **Key**: `session_key_2` **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "imageHash": "", "nonce": "", "appId": "", "sodium": "", "routePath": "P41", "xxid": "" } } ``` **Response**: ```json { "success": true, "reasonCode": "201", "reasonText": "Image Found", "profileImage": "" } ``` --- ### [8] OTP Verification (Login) — `sfunc=n`, `routePath: A42` **Key**: `session_key_2` **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "otp": "<6-digit OTP>", "uname": "", "otpType": "3", "nonce": "", "appId": "", "sodium": "", "routePath": "A42", "xxid": "" } } ``` --- ## Password Hashing (`pgf03`) The password is never sent in plaintext. The scheme prevents replay attacks by mixing in a server-provided salt and a client-generated random salt. ``` pgf03 = SHA256( clientSalt + SHA256( userSalt + SHA256( password ) ) ) ``` All SHA256 values are uppercase hex strings. ```python import hashlib def compute_pgf03(password: str, user_salt: str, client_salt: str) -> str: h1 = hashlib.sha256(password.encode()).hexdigest().upper() h2 = hashlib.sha256((user_salt + h1).encode()).hexdigest().upper() h3 = hashlib.sha256((client_salt + h2).encode()).hexdigest().upper() return h3 ``` --- ## Route Paths Summary | routePath | sfunc | Description | |---|---|---| | `S40` | `r` or `i` | DH key exchange | | `A44` | `n` | Get auth type / userSalt | | `A41` | `n` | Regular login initialization | | `A42` | `n` | OTP verification (regular login) | | `C41` | `n` | Device registration initialization | | `C42` | `n` | OTP verification (registration) | | `P41` | `n` | Get profile image | | `P40` | `n` | Update profile image | | `P42` | `n` | Delete profile image |