hi, not sure if this post is best here, or on the Sui discord, Slush web wallet: signPersonalMessage builds an expired zkLogin envelope
Evidence package - 25 Aug 2026. Ready to file to Slush / Mysten Labs.
One sentence: Slush web’s signPersonalMessage wraps a cryptographically
VALID Secp256r1 ephemeral signature in a zkLogin envelope whose proof carries
maxEpoch = 8 (current Sui mainnet epoch: 1229), so every correct verifier
refuses it - while the SAME wallet’s transactions carry maxEpoch = 1230 and
execute fine on mainnet.
The contrast, from the same wallet on the same day
| Surface | zkLogin maxEpoch | Ephemeral scheme | Verdict |
|---|---|---|---|
Transaction GN4fTRkFzYF8SguVJf7w6LUuuoC6MFRq8e9hCGWPRwtX (register, executed on mainnet 24 Aug 13:08 UTC) |
1230 | 2 (Secp256r1) | valid, executed |
Transaction B25LUBtAPRuHFjM4yCTQTLU8Pk56W2gWSeBddp4HxHGv (certify+complete, executed 13:15 UTC) |
1230 | 2 (Secp256r1) | valid, executed |
| Personal message (Seal SessionKey certificate, taps on 24-25 Aug) | 8 | 2 (Secp256r1) | refused by every verifier |
Both transaction envelopes read directly from chain (GraphQL
transaction.signatures.signatureBytes, parsed with
@mysten/sui/zklogin parseZkLoginSignature).
The personal-message evidence (QA build p20260825c, 25 Aug 05:23 UTC)
- Wallet: Slush web,
com.mystenlabs.suiwallet.web, registered via
@mysten/slush-wallet1.1.13 (1.1.18 is byte-identical in both sign
methods - the envelope is built by the my.slush.app popup, not the npm
package). Account: zkLogin,0xf4502a93...aa3c1c2. - Message: a standard Seal SessionKey personal message (“Accessing keys of
package 0x682d… for 10 mins from … session key …”), produced by
@mysten/seal1.3.8getPersonalMessage(). signPersonalMessagereturnedbytesexactly matching the request
(bytesMatch: true) and a 975-byte flag-5 (zkLogin) signature.- Parsed envelope: ephemeral userSignature flag 2 (Secp256r1, 98 bytes),
maxEpoch = 8, ephemeral public key
AxprBmSsHCxehsu1hya6JhKG8kf+ADiGBP+lMSse/K+x. - The inner ephemeral signature verifies over the exact message bytes via
Mysten’s ownSecp256r1PublicKey.verifyPersonalMessage(rawBytes: true;
the base64-string variant is false - the wallet signed the right bytes). - The complete envelope is refused by:
@mysten/suiverifyPersonalMessageSignaturevia the fullnode
signature-verification service: “Signature is not valid for the provided
message” (a clean invalid, not an environmental error);- Seal key servers (which delegate zkLogin PM verification to their
fullnode): “User signature on the session key is invalid”.
Why every verifier is RIGHT to refuse
A proof capped at epoch 8 expired roughly 1,221 epochs ago. The verifiers are
behaving correctly; the envelope is invalid as constructed. The bug is in the
my.slush.app popup’s personal-message flow: it attaches stale or placeholder
proof material (maxEpoch 8) around a fresh, correct ephemeral signature,
instead of the current session proof it uses for transactions.
Impact
Any dapp that verifies Slush-web personal-message signatures fails for every
zkLogin account: Seal SessionKey certificates (this product’s retrieval
path), sign-in-with-Sui flows, any offchain signature check. Transactions are
unaffected.
Instrument notes (for reproduction)
The probe ran in-page: parse flag-5 signature body with
parseZkLoginSignature, extract userSignature, verify the ephemeral layer
with the SDK’s Ed25519PublicKey/Secp256r1PublicKey
.verifyPersonalMessage against the requested bytes and a base64-string
variant. The probe was validated on synthetic signatures in both directions
before use. Known adjacent issue class: MystenLabs/sui#18949.