# Login Flow MIB uses a two-phase authentication model: | Phase | Trigger | Key used | |---|---|---| | **Device Registration** | First time this device+account pair is seen | `DEFAULT_KEY` → DH session key | | **Regular Login** | Every subsequent login (stored `key1`/`key2`) | `key1` → DH session key | --- ## Password Hashing (`pgf03`) The password is never sent in plaintext. Required by both `C41` (registration) and `A41` (login). ``` pgf03 = SHA256( clientSalt + SHA256( userSalt + SHA256( password ) ) ) ``` All SHA-256 values are uppercase hex strings. `clientSalt` is a fresh random 32-character alphanumeric string each time. ```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() return hashlib.sha256((client_salt + h2).encode()).hexdigest().upper() ``` --- ## Device Registration Flow (first time only) ``` [0] sfunc=r DEFAULT_KEY → DH exchange → derive session_key_1, get xxid + nonceGenerator [1] sfunc=n A44 → get userSalt [2] sfunc=n C41 → submit credentials → returns key1, key2 (persist!) [3] sfunc=n C42 → verify OTP [4–8] regular login (below) using the key1/key2 just received ``` --- ## Regular Login Flow ``` [0] sfunc=i key1 → DH exchange → derive session_key_2, get xxid + nonceGenerator [1] sfunc=n A44 → get userSalt [2] sfunc=n A41 → submit credentials → returns otpTypes, email, uuid [3] sfunc=n P41 → fetch profile image (optional) [4] sfunc=n A42 → verify OTP → session established [5] sfunc=n P47 → select profile → returns accountBalance array ``` --- ## Step-by-Step Reference ### [0] Initial Key Exchange — `sfunc=r` **Key**: `DEFAULT_KEY = 8M3L9SBF1AC4FRE56788M3L9SBF1AC4FRE5678` **Request** (outer + inner are encrypted together): ```json { "sfunc": "r", "data": { "cmod": "", "appId": "IOS17.2-<15 random alphanumeric chars>", "routePath": "S40", "sodium": "", "xxid": "" } } ``` **Response** (decrypted with `DEFAULT_KEY`): ```json { "success": true, "reasonCode": "201", "reasonText": "Key generated successfully.", "smod": "", "nonceGenerator": "", "xxid": "", "sodium": "", "encMethod": 2 } ``` After: `session_key = derive_session_key(int(smod))`. Save `xxid` and `nonceGenerator`. --- ### [1] Get Auth Type — `sfunc=n`, `routePath: A44` **Key**: session key **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "uname": "", "nonce": "", "appId": "", "sodium": "", "routePath": "A44", "xxid": "" } } ``` **Response**: ```json { "success": true, "data": [{ "loginType": "1", "userSalt": "" }] } ``` Use `userSalt` in `pgf03` computation. --- ### [2a] Device Registration Init — `sfunc=n`, `routePath: C41` _First-time only._ **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "uname": "", "pgf03": "", "clientSalt": "", "nonce": "", "appId": "", "sodium": "", "routePath": "C41", "xxid": "" } } ``` **Response**: ```json { "success": true, "reasonCode": "201", "primaryOTPType": "3", "otpTypes": [2, 3], "fullName": "", "customerImgHash": "" } ``` --- ### [3a] OTP Verification (Registration) — `sfunc=n`, `routePath: C42` _First-time only. Receive and persist `key1`/`key2`._ **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", "data": [{ "key1": "", "key2": "", "appId": "", "encryptionMethod": "2" }] } ``` `key1` and `key2` are long-lived device credentials. Store them securely on the device. --- ### [0b] Authenticated Key Exchange — `sfunc=i` _Regular login. `key2` is a separate unencrypted outer field._ **Key**: `key1` Form body: `key2=&sfunc=i&data=` **Encrypted payload**: ```json { "sfunc": "i", "key2": "", "data": { "cmod": "", "appId": "", "routePath": "S40", "sodium": "", "xxid": "" } } ``` **Response** (decrypted with `key1`): ```json { "success": true, "smod": "", "nonceGenerator": "", "xxid": "", "encMethod": 2 } ``` After: derive new session key, replace `xxid` and `nonceGenerator`. --- ### [2b] Regular Login Init — `sfunc=n`, `routePath: A41` **Key**: session key **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", "primaryOTPType": "3", "otpTypes": [2, 3], "email": "", "uuid": "", "uuid2": "" } ``` --- ### [3b] Get Profile Image — `sfunc=n`, `routePath: P41` Optional. Fetch the user's avatar to display on the OTP screen. **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "imageHash": "", "nonce": "", "appId": "", "sodium": "", "routePath": "P41", "xxid": "" } } ``` **Response**: ```json { "success": true, "reasonCode": "201", "profileImage": "" } ``` --- ### [4b] OTP Verification (Login) — `sfunc=n`, `routePath: A42` **Request**: ```json { "sfunc": "n", "xxid": "", "data": { "otp": "<6-digit OTP>", "uname": "", "otpType": "3", "nonce": "", "appId": "", "sodium": "", "routePath": "A42", "xxid": "" } } ``` After successful `A42`, the `xxid` and `nonceGenerator` from the `sfunc=i` response become the WebView session cookies. See [README](README.md) for the cookie format. --- ### [5] Select Profile — `sfunc=n`, `routePath: P47` See [03-accounts.md](03-accounts.md) for the full P47 reference. ---   --- [← Encryption](01-encryption.md)     **Next →** [Accounts](03-accounts.md)