element-hq / element-android

A Matrix collaboration client for Android.
https://element.io/
Apache License 2.0
3.28k stars 678 forks source link

Unable to setup cross-signing keys #8864

Open hardcore-sushi opened 2 weeks ago

hardcore-sushi commented 2 weeks ago

Steps to reproduce

  1. Install conduit server
  2. Login with element android
  3. Click Settings > Security & Privacy > Cross-Signing > Reset Keys
  4. Enter the password
  5. Click Continue

Outcome

What did you expect?

Cross-signing keys to be reset.

What happened instead?

Element show the error message: "Failed to set up Cross Signing". Conduit prints:

WARN http_request{path=/_matrix/client/unstable/keys/device_signing/upload}: conduit::api::ruma_wrapper::axum: try_from_http_request failed: Deserialization(Json(Error("missing field `identifier`", line: 1, column: 119)))

The exact request sent by Element is:

{
  "auth": {
    "password": "<PASSWORD>",
    "session": "8ndGKxkyPSBn81Y88SMBDtzRkUVDTned",
    "type": "m.login.password",
    "user": "@bob:<HOMESERVER>"
  },
  "master_key": {
    "keys": {
      "ed25519:yG5KfTv8UlwauaUFQ4sgpqcKArVfo03SmAmBB3xDObU": "yG5KfTv8UlwauaUFQ4sgpqcKArVfo03SmAmBB3xDObU"
    },
    "signatures": {
      "@bob:<HOMESERVER>": {
        "ed25519:LD0549yCCK": "U41xp/sIh23JiAKPk2rgNPpfNLDZ61+SvJag1n7AzBzht3QJtBIcOPRaXCHA/Yk5bQRi3QuczDnD/b54HHaQAg",
        "ed25519:yG5KfTv8UlwauaUFQ4sgpqcKArVfo03SmAmBB3xDObU": "YrBziZkOCifL1+gVChTozjLu45kc4CJpiAYEifMqbWl9dCOEigU2iUQ6aTi/HLUZlkABtoh6vOZGR4TIIhdMDQ"
      }
    },
    "usage": [
      "master"
    ],
    "user_id": "@bob:<HOMESERVER>"
  },
  "self_signing_key": {
    "keys": {
      "ed25519:oOICLRIankieFLVafqVTuB5aKzCUD1/vcw5QVYolvfY": "oOICLRIankieFLVafqVTuB5aKzCUD1/vcw5QVYolvfY"
    },
    "signatures": {
      "@bob:<HOMESERVER>": {
        "ed25519:yG5KfTv8UlwauaUFQ4sgpqcKArVfo03SmAmBB3xDObU": "X61b9girYvqVCUVZM7JfBm5vuTCgpIdlaCrvr6kPTFN0BtgL/4Hy9amWECKvf/1WLk4xp3yh+FjtEDHj5gphAw"
      }
    },
    "usage": [
      "self_signing"
    ],
    "user_id": "@bob:<HOMESERVER>"
  },
  "user_signing_key": {
    "keys": {
      "ed25519:4iFzyGjcNMeKcQkrNiwiOPU9ZdaktgNbnrNmkVKnRTw": "4iFzyGjcNMeKcQkrNiwiOPU9ZdaktgNbnrNmkVKnRTw"
    },
    "signatures": {
      "@bob:<HOMESERVER>": {
        "ed25519:yG5KfTv8UlwauaUFQ4sgpqcKArVfo03SmAmBB3xDObU": "6yfZUO6ww+KsRggf7Aq4G1rH+thXHZkvfvglS6sb2DJc1c9jiOCTXD/tFe+Qr8XGe3NrZGG6qJLRiC0t/3GJAQ"
      }
    },
    "usage": [
      "user_signing"
    ],
    "user_id": "@bob:<HOMESERVER>"
  }
}

As you can see, the user field is directly located under the auth object, whereas element-web (which works) sends this auth object:

  "auth": {
    "password": "<PASSWORD>",
    "session": "8ndGKxkyPSBn81Y88SMBDtzRkUVDTned",
    "type": "m.login.password",
    "identifier": {
      "type": "m.id.user",
      "user": "@bob:<HOMESERVER>"
    }
  }

It seems that element-android does not respect the Matrix spec:

Your phone model

No response

Operating system version

No response

Application version and app store

1.6.18 from F-Droid

Homeserver

conduit v0.8.0

Will you send logs?

Yes

Are you willing to provide a PR?

No

hardcore-sushi commented 15 hours ago

This issue actually affects more that just the reset cross-signing key feature.

If the first login to the account is made with Element (typically when registering), it sends the same request to configure the initial cross-signing keys. Due to the malformed request payload, the setup fails, although Element reports it as properly enabled in the settings. Consequently, the session is reported as not verified (red shield) in encrypted chats, and any keys verification with other accounts is thus prevented.

For people stumbled upon this issue and looking for a workaround, you can add a rule in your nginx reverse proxy (if you use one) to fix the JSON payload. You need lua support with the cjson library. Here is an example configuration snippet:

location = /_matrix/client/unstable/keys/device_signing/upload {
  if ($request_method = POST) {
    access_by_lua_block {
      local cjson = require "cjson"
      ngx.req.read_body()
      local data = cjson.decode(ngx.req.get_body_data())
      if data["auth"] ~= nil and data.auth["user"] ~= nil then
        data.auth.identifier = {
          type = "m.id.user",
          user = data.auth.user,
        };
        ngx.req.set_body_data(cjson.encode(data))
      end
    }
  }
  proxy_pass http://<CONDUIT ENDPOINT>$request_uri;
  # ...
}