MuntashirAkon/AppManager · critical · IOException
Unauthorized server: HMAC mismatch.
Error message
Unauthorized server: HMAC mismatch.
What it means
During the challenge-response handshake the client verifies the server by checking that HMAC_S equals HMAC(token, Nonce_C). If MessageDigest.isEqual fails, the peer cannot prove possession of the shared token, so shakeHands() throws IOException("Unauthorized server: HMAC mismatch.") and the connection is dropped as a rogue server.
Source
Thrown at libserver/src/main/java/io/github/muntashirakon/AppManager/server/common/DataTransmission.java:184
*/
public void shakeHands(@NonNull String token, Role role) throws IOException {
Objects.requireNonNull(token);
if (role == Role.Client) {
FLog.log("DataTransmission#shakeHands: Client protocol: " + PROTOCOL_VERSION);
// Send protocol version and client challenge (Nonce_C)
sendMessage(PROTOCOL_VERSION.getBytes(StandardCharsets.UTF_8));
byte[] nonceC = AuthUtils.generateNonce();
sendMessage(nonceC);
// Receive server's HMAC proof and server nonce (HMAC_S, None_S)
byte[] serverHmac = readMessage();
byte[] nonceS = readMessage();
// Validate server (HMAC_S == HMAC(token, Nonce_C)?)
byte[] expectedServerHmac = AuthUtils.calculateHmac(token, nonceC);
if (!MessageDigest.isEqual(serverHmac, expectedServerHmac)) {
FLog.log("DataTransmission#shakeHands: Rogue server detected! Connection dropped.");
throw new IOException("Unauthorized server: HMAC mismatch.");
}
// Prove legitimacy of client to the server (HMAC_C = HMAC(token, Nonce_S)
byte[] clientHmac = AuthUtils.calculateHmac(token, nonceS);
sendMessage(clientHmac);
} else if (role == Role.Server) {
FLog.log("DataTransmission#shakeHands: Server protocol: " + PROTOCOL_VERSION);
// Receive protocol version and client nonce (Nonce_C)
String clientProtocol = new String(readMessage(), StandardCharsets.UTF_8);
if (!PROTOCOL_VERSION.equals(clientProtocol)) {
throw new ProtocolVersionException("Client protocol version: " + clientProtocol + ", " +
"Server protocol version: " + PROTOCOL_VERSION);
}
byte[] nonceC = readMessage();
// Prove legitimacy of server to the client (HMAC_S = HMAC(token, Nonce_C))
byte[] serverHmac = AuthUtils.calculateHmac(token, nonceC);View on GitHub (pinned to 0152f468fc)
Solutions
- Ensure both client and server derive the token from the same source (same shared secret file/config)
- Restart/rebind the privileged service so it picks up the current token
- Verify both sides use the same HMAC algorithm and nonce byte encoding (AuthUtils.calculateHmac)
- Re-run the full handshake after any token rotation
Example fix
// before DataTransmission t = new DataTransmission(in, out, oldToken); t.shakeHands(); // Unauthorized server: HMAC mismatch. // after byte[] token = readCurrentSharedToken(); // same source as the server DataTransmission t = new DataTransmission(in, out, token); t.shakeHands();
Defensive patterns
Strategy: try-catch
Try / catch
try {
transmission.shakeHands();
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().contains("Unauthorized server")) {
// drop connection; refresh token and reconnect
token = readCurrentSharedToken();
transmission = reconnectAndHandshake(token);
} else throw e;
} Prevention
- Ensure client and server read the token from the same source
- Rotate and redistribute tokens atomically on updates/reinstalls
- Use identical HMAC computation and nonce encodings on both ends
- Treat any HMAC failure as a security event: drop the connection immediately
When it happens
Trigger: Connecting to a server that uses a different token/secret than the client, a man-in-the-middle without the token, a version mismatch in HMAC computation (different nonce encoding), or relaying through a modified root service.
Common situations: Stale or regenerated shared token between app and privileged service after reinstall/update; connecting to the wrong server socket; custom server implementations that compute the HMAC incorrectly.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unauthorized client: HMAC mismatch.
- Error! Invalid characters in arguments.\n
- Signing info verification failed.\nInstalled: ${certChecksum
- Zip slip vulnerability detected!\nExpected dest: " + new Fil
- Zip slip vulnerability detected!\nExpected dest: " + new Fil
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/d8a041d650132945.
Report an issue: GitHub.