package roomserver import ( "context" "fmt" "github.com/dariuszjaniak/warp/server/internal/matrix/auth" "github.com/dariuszjaniak/warp/server/internal/matrix/event" "github.com/dariuszjaniak/warp/server/internal/matrix/signing" ) // eventVerifier checks an event's signature from a named server. // // This server's own signatures are checked against the key in hand, which // is what keeps every rule that needs a signature — a restricted join // authorised by a local user, chiefly — working with federation off. // Anybody else's go to the key ring, and only while federation is on: with // it off there is no way to fetch a key, and a signature that cannot be // checked is refused rather than waved through, the same answer the switch // gives everywhere else. type eventVerifier struct { serverName string key *signing.KeyPair remote auth.Verifier enabled func() bool } // verifier returns the Verifier the authorisation rules are given. func (r *API) verifier() *eventVerifier { return &eventVerifier{ serverName: r.serverName, key: r.signingKey, remote: r.keys, enabled: r.federationEnabled, } } // VerifyEventSignature implements auth.Verifier. func (v *eventVerifier) VerifyEventSignature(ctx context.Context, serverName string, e *event.Event) error { if serverName == v.serverName { return e.VerifySignature(v.serverName, v.key.KeyID(), v.key.Public) } if v.remote == nil || !v.enabled() { return fmt.Errorf("%w: cannot verify a signature from %s", ErrFederationDisabled, serverName) } return v.remote.VerifyEventSignature(ctx, serverName, e) } // verifyIncoming checks an event from another server before anything else // looks at it: it must be signed by its sender's server, and its content // must match its hash. // // A content hash mismatch is not a rejection. The signature covers only // what a redaction keeps, so an event whose signature verifies and whose // hash does not is an event somebody has redacted — legitimately, or by // tampering with the parts the signature does not cover — and the spec's // answer to both is the same: keep the redacted form. func (r *API) verifyIncoming(ctx context.Context, e *event.Event) (*event.Event, error) { sender, err := e.SenderServer() if err != nil { return nil, fmt.Errorf("%w: %s: %v", ErrBadEvent, e.EventID(), err) } if err := r.verifier().VerifyEventSignature(ctx, sender, e); err != nil { return nil, fmt.Errorf("%w: %s is not signed by %s: %v", ErrBadEvent, e.EventID(), sender, err) } if err := e.VerifyContentHash(); err != nil { redacted, rerr := e.Redact() if rerr != nil { return nil, fmt.Errorf("%w: %s: %v", ErrBadEvent, e.EventID(), rerr) } r.log.Info("event failed its content hash; using its redacted form", "event_id", e.EventID(), "sender", e.Sender()) return redacted, nil } return e, nil }