Skip to content

Commit

Permalink
Add tests for msc3967 (#713)
Browse files Browse the repository at this point in the history
* Add tests for msc3967

* Additional idempotency check
  • Loading branch information
kegsay authored Apr 15, 2024
1 parent e323c79 commit 31d4f5d
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
11 changes: 11 additions & 0 deletions tests/msc3967/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package tests

import (
"testing"

"github.com/matrix-org/complement"
)

func TestMain(m *testing.M) {
complement.TestMain(m, "msc3967")
}
114 changes: 114 additions & 0 deletions tests/msc3967/msc3967_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package tests

import (
"crypto/ed25519"
"encoding/base64"
"encoding/json"
"testing"

"github.com/matrix-org/complement"
"github.com/matrix-org/complement/client"
"github.com/matrix-org/complement/helpers"
"github.com/matrix-org/complement/match"
"github.com/matrix-org/complement/must"
"github.com/matrix-org/gomatrixserverlib"
"github.com/tidwall/gjson"
)

// MSC3967: Do not require UIA when first uploading cross signing keys
// Tests that:
// - No UIA required on POST /_matrix/client/v3/keys/device_signing/upload
// - If x-signing keys exist, does require UIA.
// - If reupload exactly the same x-signing keys, does not require UIA.
func TestMSC3967(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer deployment.Destroy(t)

privKey := ed25519.NewKeyFromSeed([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32})
pubKey := base64.RawStdEncoding.EncodeToString(privKey.Public().(ed25519.PublicKey))
t.Logf("pub key => %s", pubKey)
password := "helloworld"
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{
Password: password,
})

// uploading x-signing master key does not require UIA (so no 401)
uploadBody := map[string]any{
"master_key": map[string]any{
"user_id": alice.UserID,
"usage": []string{"master"},
"keys": map[string]string{
"ed25519:" + pubKey: pubKey,
},
},
}
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "keys", "device_signing", "upload"}, client.WithJSONBody(t, uploadBody))

// reuploading the exact same request does not require UIA (idempotent)
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "keys", "device_signing", "upload"}, client.WithJSONBody(t, uploadBody))

// trying to replace the master key requires UIA
pubKey2, _, err := ed25519.GenerateKey(nil)
must.NotError(t, "failed to generate 2nd key", err)
pubKey2Base64 := base64.RawStdEncoding.EncodeToString(pubKey2)
t.Logf("pub key 2 => %s", pubKey2Base64)
res := alice.Do(t, "POST", []string{"_matrix", "client", "v3", "keys", "device_signing", "upload"}, client.WithJSONBody(t, map[string]any{
"master_key": map[string]any{
"user_id": alice.UserID,
"usage": []string{"master"},
"keys": map[string]string{
"ed25519:" + pubKey2Base64: pubKey2Base64,
},
},
}))
must.MatchResponse(t, res, match.HTTPResponse{
StatusCode: 401,
JSON: []match.JSON{
match.JSONKeyPresent("flows"),
},
})

// adding a key does require UIA
ssKey, _, err := ed25519.GenerateKey(nil)
must.NotError(t, "failed to generate self-signing key", err)
ssKey64 := base64.RawStdEncoding.EncodeToString(ssKey)
t.Logf("self-signing key => %s", ssKey64)
// replace the body so no master_key in this request, because if we include it
// Synapse will 500 as I guess you can't replace it?!
uploadBody = map[string]any{
"self_signing_key": map[string]any{
"user_id": alice.UserID,
"usage": []string{"self_signing"},
"keys": map[string]string{
"ed25519:" + ssKey64: ssKey64,
},
},
}
toSign, err := json.Marshal(uploadBody["self_signing_key"])
must.NotError(t, "failed to marshal req body", err)
signedBody, err := gomatrixserverlib.SignJSON(alice.UserID, gomatrixserverlib.KeyID("ed25519:"+pubKey), privKey, toSign)
must.NotError(t, "failed to sign json", err)
uploadBody["self_signing_key"] = json.RawMessage(signedBody)
res = alice.Do(t, "POST", []string{"_matrix", "client", "v3", "keys", "device_signing", "upload"}, client.WithJSONBody(t, uploadBody))
body := must.MatchResponse(t, res, match.HTTPResponse{
StatusCode: 401,
JSON: []match.JSON{
match.JSONKeyPresent("flows"),
},
})

// but if we UIA then it should work.
uploadBody["auth"] = map[string]any{
"type": "m.login.password",
"identifier": map[string]any{
"type": "m.id.user",
"user": alice.UserID,
},
"password": password,
"session": gjson.ParseBytes(body).Get("session").Str,
}
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "keys", "device_signing", "upload"}, client.WithJSONBody(t, uploadBody))

// ensure the endpoint remains idempotent with the auth dict
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "keys", "device_signing", "upload"}, client.WithJSONBody(t, uploadBody))
}

0 comments on commit 31d4f5d

Please sign in to comment.