This commit is contained in:
+25
-6
@@ -14,17 +14,17 @@ MIB uses a two-phase authentication model:
|
||||
The password is never sent in plaintext. Required by both `C41` (registration) and `A41` (login).
|
||||
|
||||
```
|
||||
pgf03 = SHA256( clientSalt + SHA256( userSalt + SHA256( password ) ) )
|
||||
pgf03 = SHA256( clientSalt + SHA256( SHA256( password ) + userSalt ) )
|
||||
```
|
||||
|
||||
All SHA-256 values are uppercase hex strings. `clientSalt` is a fresh random 32-character alphanumeric string each time.
|
||||
All SHA-256 values are uppercase hex strings. `clientSalt` is a fresh random 32-character alphanumeric string each time. Note the inner concat order is `passwordHash + userSalt` — getting this backwards produces a valid-looking but wrong hash.
|
||||
|
||||
```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()
|
||||
h2 = hashlib.sha256((h1 + user_salt).encode()).hexdigest().upper()
|
||||
return hashlib.sha256((client_salt + h2).encode()).hexdigest().upper()
|
||||
```
|
||||
|
||||
@@ -69,7 +69,7 @@ def compute_pgf03(password: str, user_salt: str, client_salt: str) -> str:
|
||||
"cmod": "<G^A mod P as decimal string>",
|
||||
"appId": "IOS17.2-<15 random alphanumeric chars>",
|
||||
"routePath": "S40",
|
||||
"sodium": "<random 20-bit int as string>",
|
||||
"sodium": "<random int in [1_000_000, 16_000_000)>",
|
||||
"xxid": "<random 40-bit int as string>"
|
||||
}
|
||||
}
|
||||
@@ -218,7 +218,7 @@ Form body: `key2=<key2>&sfunc=i&data=<encrypted payload>`
|
||||
"cmod": "<G^A mod P>",
|
||||
"appId": "<appId>",
|
||||
"routePath": "S40",
|
||||
"sodium": "<random 20-bit int>",
|
||||
"sodium": "<random int in [1_000_000, 16_000_000)>",
|
||||
"xxid": "<random 40-bit int>"
|
||||
}
|
||||
}
|
||||
@@ -272,10 +272,29 @@ After: derive new session key, replace `xxid` and `nonceGenerator`.
|
||||
"otpTypes": [2, 3],
|
||||
"email": "<masked email>",
|
||||
"uuid": "<uuid1>",
|
||||
"uuid2": "<uuid2>"
|
||||
"uuid2": "<uuid2>",
|
||||
"operatingProfiles": [
|
||||
{
|
||||
"customerProfileId": "...",
|
||||
"annexId": "...",
|
||||
"customerId": "...",
|
||||
"name": "...",
|
||||
"cifType": "...",
|
||||
"profileType": "...",
|
||||
"color": "...",
|
||||
"customerImage": "<image hash, may be missing/blank>"
|
||||
}
|
||||
],
|
||||
"profileSelected": false,
|
||||
"selectedProfileId": "",
|
||||
"accountBalance": []
|
||||
}
|
||||
```
|
||||
|
||||
**Single-profile fast-path** — if the account has exactly one operating profile, the server returns `profileSelected: true`, populates `selectedProfileId`, and includes a non-empty `accountBalance` array in the A41 response itself. In that case the [P47](03-accounts.md) call is **skipped** and balances are read from this response (see `MibLoginFlow.kt:150-184`).
|
||||
|
||||
> **Field naming**: the image hash field on each `operatingProfiles[]` entry is `customerImage`. The same conceptual value is called `customerImgHash` in the contacts API response — the two endpoints disagree on the field name.
|
||||
|
||||
---
|
||||
|
||||
### [3b] Get Profile Image — `sfunc=n`, `routePath: P41`
|
||||
|
||||
Reference in New Issue
Block a user