mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-08 14:43:09 -06:00
* Verify that the user ID for registration matches the spec, and the auth data (#10) * Blacklist some sytest tests that are failing in our environment * Commenting out test that isn't reliably passing or failing, probably a race * refresh latest dendrite main * pull latest from dendrite-fork subtree * refresh latest dendrite main * pull dendrite subtree and resolve merge conflicts * check that userID matches the signed message * verify that the user ID for registration is CAIP-10 compliant and MXID compliant * removed space Co-authored-by: Brian Meek <brian@hntlabs.com> Co-authored-by: Tak Wai Wong <takwaiw@gmail.com> * Fix nats.go commit (#2540) Signed-off-by: Jean Lucas <jean@4ray.co> * Don't return `end` if there are not more messages (#2542) * Be more spec compliant * Move lazyLoadMembers to own method * Return an error if trying to invite a malformed user ID (#2543) * Add `evacuateUser` endpoint, use it when deactivating accounts (#2545) * Add `evacuateUser` endpoint, use it when deactivating accounts * Populate the API * Clean up user devices when deactivating * Include invites, delete pushers * Silence presence logs (#2547) * Takwaiw/fix concurrent registration bug (#12) * fix concurrent registration bug. Rename decentralizedid * remove unused module * add regressed test to blacklist Co-authored-by: Tak Wai Wong <takwaiw@gmail.com> Co-authored-by: Brian Meek <brian@hntlabs.com> Co-authored-by: Tak Wai Wong <takwaiw@gmail.com> Co-authored-by: Jean Lucas <jean@4ray.co> Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com> Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com>
164 lines
5 KiB
Go
164 lines
5 KiB
Go
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package auth
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
|
"github.com/matrix-org/dendrite/clientapi/userutil"
|
|
"github.com/matrix-org/dendrite/setup/config"
|
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
|
"github.com/spruceid/siwe-go"
|
|
)
|
|
|
|
type LoginPublicKeyEthereum struct {
|
|
// https://github.com/tak-hntlabs/matrix-spec-proposals/blob/main/proposals/3782-matrix-publickey-login-spec.md#client-sends-login-request-with-authentication-data
|
|
Type string `json:"type"`
|
|
UserId string `json:"user_id"`
|
|
Session string `json:"session"`
|
|
Message string `json:"message"`
|
|
Signature string `json:"signature"`
|
|
|
|
userAPI userapi.ClientUserAPI
|
|
config *config.ClientAPI
|
|
}
|
|
|
|
func CreatePublicKeyEthereumHandler(
|
|
reqBytes []byte,
|
|
userAPI userapi.ClientUserAPI,
|
|
config *config.ClientAPI,
|
|
) (*LoginPublicKeyEthereum, *jsonerror.MatrixError) {
|
|
var pk LoginPublicKeyEthereum
|
|
if err := json.Unmarshal(reqBytes, &pk); err != nil {
|
|
return nil, jsonerror.BadJSON("auth")
|
|
}
|
|
|
|
pk.config = config
|
|
pk.userAPI = userAPI
|
|
// Case-insensitive
|
|
pk.UserId = strings.ToLower(pk.UserId)
|
|
|
|
return &pk, nil
|
|
}
|
|
|
|
func (pk LoginPublicKeyEthereum) GetSession() string {
|
|
return pk.Session
|
|
}
|
|
|
|
func (pk LoginPublicKeyEthereum) GetType() string {
|
|
return pk.Type
|
|
}
|
|
|
|
func (pk LoginPublicKeyEthereum) AccountExists(ctx context.Context) (string, *jsonerror.MatrixError) {
|
|
localPart, err := userutil.ParseUsernameParam(pk.UserId, &pk.config.Matrix.ServerName)
|
|
if err != nil {
|
|
// userId does not exist
|
|
return "", jsonerror.Forbidden("the address is incorrect, or the account does not exist.")
|
|
}
|
|
|
|
res := userapi.QueryAccountAvailabilityResponse{}
|
|
if err := pk.userAPI.QueryAccountAvailability(ctx, &userapi.QueryAccountAvailabilityRequest{
|
|
Localpart: localPart,
|
|
}, &res); err != nil {
|
|
return "", jsonerror.Unknown("failed to check availability: " + err.Error())
|
|
}
|
|
|
|
if res.Available {
|
|
return "", jsonerror.Forbidden("the address is incorrect, account does not exist")
|
|
}
|
|
|
|
return localPart, nil
|
|
}
|
|
|
|
var validChainAgnosticIdRegex = regexp.MustCompile("^eip155=3a[0-9]+=3a0x[0-9a-fA-F]+$")
|
|
|
|
func (pk LoginPublicKeyEthereum) IsValidUserIdForRegistration(userId string) bool {
|
|
// Verify that the user ID is a valid one according to spec.
|
|
// https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md
|
|
|
|
// Matrix ID has additional grammar requirements for user ID.
|
|
// https://spec.matrix.org/v1.1/appendices/#user-identifiers
|
|
// Make sure disallowed characters are escaped.
|
|
// E.g. ":" is replaced with "=3a".
|
|
|
|
isValid := validChainAgnosticIdRegex.MatchString(userId)
|
|
|
|
// In addition, double check that the user ID for registration
|
|
// matches the authentication data in the request.
|
|
return isValid && userId == pk.UserId
|
|
}
|
|
|
|
func (pk LoginPublicKeyEthereum) ValidateLoginResponse() (bool, *jsonerror.MatrixError) {
|
|
// Parse the message to extract all the fields.
|
|
message, err := siwe.ParseMessage(pk.Message)
|
|
if err != nil {
|
|
return false, jsonerror.InvalidParam("auth.message")
|
|
}
|
|
|
|
// Check signature to verify message was not tempered
|
|
_, err = message.Verify(pk.Signature, (*string)(&pk.config.Matrix.ServerName), nil, nil)
|
|
if err != nil {
|
|
return false, jsonerror.InvalidSignature(err.Error())
|
|
}
|
|
|
|
// Error if the user ID does not match the signed message.
|
|
isVerifiedUserId := pk.verifyMessageUserId(message)
|
|
if !isVerifiedUserId {
|
|
return false, jsonerror.InvalidUsername(pk.UserId)
|
|
}
|
|
|
|
// Error if the chainId is not supported by the server.
|
|
if !contains(pk.config.PublicKeyAuthentication.Ethereum.ChainIDs, message.GetChainID()) {
|
|
return false, jsonerror.Forbidden("chainId")
|
|
}
|
|
|
|
// No errors.
|
|
return true, nil
|
|
}
|
|
|
|
func (pk LoginPublicKeyEthereum) CreateLogin() *Login {
|
|
identifier := LoginIdentifier{
|
|
Type: "m.id.decentralizedid",
|
|
User: pk.UserId,
|
|
}
|
|
login := Login{
|
|
Identifier: identifier,
|
|
}
|
|
return &login
|
|
}
|
|
|
|
func (pk LoginPublicKeyEthereum) verifyMessageUserId(message *siwe.Message) bool {
|
|
// Use info in the signed message to derive the expected user ID.
|
|
expectedUserId := fmt.Sprintf("eip155=3a%d=3a%s", message.GetChainID(), message.GetAddress())
|
|
|
|
// Case-insensitive comparison to make sure the user ID matches the expected
|
|
// one derived from the signed message.
|
|
return pk.UserId == strings.ToLower(expectedUserId)
|
|
}
|
|
|
|
func contains(list []int, element int) bool {
|
|
for _, i := range list {
|
|
if i == element {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|