Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 4 additions & 24 deletions fed/apgateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import (

"github.com/dimkr/tootik/ap"
"github.com/dimkr/tootik/danger"
"github.com/dimkr/tootik/data"
"github.com/dimkr/tootik/httpsig"
)

var (
Expand All @@ -50,9 +48,8 @@ func (l *Listener) handleAPGatewayPost(w http.ResponseWriter, r *http.Request) {

receiver := "ap://" + m[1]

var actor ap.Actor
var rsaPrivKeyPem, ed25519PrivKeyMultibase string
if err := l.DB.QueryRowContext(r.Context(), `select json(actor), rsaprivkey, ed25519privkey from persons where cid = ? and ed25519privkey is not null`, receiver).Scan(&actor, &rsaPrivKeyPem, &ed25519PrivKeyMultibase); errors.Is(err, sql.ErrNoRows) {
var exists int
if err := l.DB.QueryRowContext(r.Context(), `select exists (select 1 from persons where cid = ? and ed25519privkey is not null)`, receiver).Scan(&exists); err == nil && exists == 0 {
slog.Debug("Receiving user does not exist", "receiver", receiver)
w.WriteHeader(http.StatusNotFound)
return
Expand All @@ -62,28 +59,11 @@ func (l *Listener) handleAPGatewayPost(w http.ResponseWriter, r *http.Request) {
return
}

rsaPrivKey, err := data.ParseRSAPrivateKey(rsaPrivKeyPem)
if err != nil {
slog.Warn("Failed to parse RSA private key", "receiver", receiver, "error", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

ed25519PrivKey, err := data.DecodeEd25519PrivateKey(ed25519PrivKeyMultibase)
if err != nil {
slog.Warn("Failed to decode Ed25519 private key", "receiver", receiver, "error", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

l.doHandleInbox(w, r, [2]httpsig.Key{
{ID: actor.PublicKey.ID, PrivateKey: rsaPrivKey},
{ID: actor.AssertionMethod[0].ID, PrivateKey: ed25519PrivKey},
})
l.doHandleInbox(w, r)
}

func (l *Listener) handleApGatewayFollowers(w http.ResponseWriter, r *http.Request, did string) {
_, sender, err := l.verifyRequest(r, nil, ap.InstanceActor, l.ActorKeys)
_, sender, err := l.verifyRequest(r, nil, ap.InstanceActor)
if err != nil {
slog.Warn("Failed to verify followers request", "error", err)
w.WriteHeader(http.StatusUnauthorized)
Expand Down
2 changes: 1 addition & 1 deletion fed/followers.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (f partialFollowers) Digest(ctx context.Context, db *sql.DB, domain string,
func (l *Listener) handleFollowers(w http.ResponseWriter, r *http.Request) {
name := r.PathValue("username")

_, sender, err := l.verifyRequest(r, nil, ap.InstanceActor, l.ActorKeys)
_, sender, err := l.verifyRequest(r, nil, ap.InstanceActor)
if err != nil {
slog.Warn("Failed to verify followers request", "error", err)
w.WriteHeader(http.StatusUnauthorized)
Expand Down
39 changes: 10 additions & 29 deletions fed/inbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package fed
import (
"bytes"
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -219,8 +218,8 @@ func (l *Listener) validateActivity(activity *ap.Activity, origin string, depth
return nil
}

func (l *Listener) fetchObject(ctx context.Context, id string, keys [2]httpsig.Key) (bool, []byte, error) {
resp, err := l.Resolver.Get(ctx, keys, id)
func (l *Listener) fetchObject(ctx context.Context, id string) (bool, []byte, error) {
resp, err := l.Resolver.Get(ctx, l.ActorKeys, id)
if err != nil {
if resp != nil && (resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusGone) {
return false, nil, err
Expand Down Expand Up @@ -282,15 +281,14 @@ func (l *Listener) fetchObject(ctx context.Context, id string, keys [2]httpsig.K
}

func (l *Listener) handleSharedInbox(w http.ResponseWriter, r *http.Request) {
l.doHandleInbox(w, r, l.ActorKeys)
l.doHandleInbox(w, r)
}

func (l *Listener) handleInbox(w http.ResponseWriter, r *http.Request) {
receiver := r.PathValue("username")

var actor ap.Actor
var rsaPrivKeyPem, ed25519PrivKeyMultibase string
if err := l.DB.QueryRowContext(r.Context(), `select json(actor), rsaprivkey, ed25519privkey from persons where actor->>'$.preferredUsername' = ? and ed25519privkey is not null`, receiver).Scan(&actor, &rsaPrivKeyPem, &ed25519PrivKeyMultibase); errors.Is(err, sql.ErrNoRows) {
var exists int
if err := l.DB.QueryRowContext(r.Context(), `select exists (select 1 from persons where actor->>'$.preferredUsername' = ? and ed25519privkey is not null)`, receiver).Scan(&exists); err == nil && exists == 0 {
slog.Debug("Receiving user does not exist", "receiver", receiver)
w.WriteHeader(http.StatusNotFound)
return
Expand All @@ -300,27 +298,10 @@ func (l *Listener) handleInbox(w http.ResponseWriter, r *http.Request) {
return
}

rsaPrivKey, err := data.ParseRSAPrivateKey(rsaPrivKeyPem)
if err != nil {
slog.Warn("Failed to parse RSA private key", "receiver", receiver, "error", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

ed25519PrivKey, err := data.DecodeEd25519PrivateKey(ed25519PrivKeyMultibase)
if err != nil {
slog.Warn("Failed to decode Ed25519 private key", "receiver", receiver, "error", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

l.doHandleInbox(w, r, [2]httpsig.Key{
{ID: actor.PublicKey.ID, PrivateKey: rsaPrivKey},
{ID: actor.AssertionMethod[0].ID, PrivateKey: ed25519PrivKey},
})
l.doHandleInbox(w, r)
}

func (l *Listener) doHandleInbox(w http.ResponseWriter, r *http.Request, keys [2]httpsig.Key) {
func (l *Listener) doHandleInbox(w http.ResponseWriter, r *http.Request) {
if r.ContentLength > l.Config.MaxRequestBodySize {
slog.Warn("Ignoring big request", "size", r.ContentLength)
w.WriteHeader(http.StatusRequestEntityTooLarge)
Expand Down Expand Up @@ -359,7 +340,7 @@ func (l *Listener) doHandleInbox(w http.ResponseWriter, r *http.Request, keys [2
var sig *httpsig.Signature
if activity.Proof != (ap.Proof{}) {
// if activity has an integrity proof, pretend it was sent by its actor even if forwarded by another
sender, err = l.verifyProof(r.Context(), activity.Proof, &activity, rawActivity, flags, keys)
sender, err = l.verifyProof(r.Context(), activity.Proof, &activity, rawActivity, flags)
if err != nil {
slog.Warn("Failed to verify integrity proof", "activity", &activity, "proof", &activity.Proof, "error", err)
w.WriteHeader(http.StatusUnauthorized)
Expand All @@ -374,7 +355,7 @@ func (l *Listener) doHandleInbox(w http.ResponseWriter, r *http.Request, keys [2
json.NewEncoder(w).Encode(map[string]any{"error": "integrity proof is required"})
return
} else {
sig, sender, err = l.verifyRequest(r, rawActivity, flags, keys)
sig, sender, err = l.verifyRequest(r, rawActivity, flags)
if err != nil {
if errors.Is(err, ErrActorGone) {
w.WriteHeader(http.StatusAccepted)
Expand Down Expand Up @@ -491,7 +472,7 @@ func (l *Listener) doHandleInbox(w http.ResponseWriter, r *http.Request, keys [2

slog.Info("Fetching forwarded object", "activity", &activity, "id", id, "sender", sender.ID)

if exists, fetched, err := l.fetchObject(r.Context(), id, keys); !exists && queued.Type == ap.Delete {
if exists, fetched, err := l.fetchObject(r.Context(), id); !exists && queued.Type == ap.Delete {
queued = &ap.Activity{
ID: queued.ID,
Type: ap.Delete,
Expand Down
12 changes: 6 additions & 6 deletions fed/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func getKeyByID(actor *ap.Actor, keyID string) (ed25519.PublicKey, error) {
return nil, fmt.Errorf("key %s does not exist", keyID)
}

func (l *Listener) verifyRequest(r *http.Request, body []byte, flags ap.ResolverFlag, keys [2]httpsig.Key) (*httpsig.Signature, *ap.Actor, error) {
func (l *Listener) verifyRequest(r *http.Request, body []byte, flags ap.ResolverFlag) (*httpsig.Signature, *ap.Actor, error) {
sig, err := httpsig.Extract(r, body, l.Domain, time.Now(), l.Config.MaxRequestAge)
if err != nil {
return nil, nil, fmt.Errorf("failed to verify message: %w", err)
Expand All @@ -79,7 +79,7 @@ func (l *Listener) verifyRequest(r *http.Request, body []byte, flags ap.Resolver
return nil, nil, fmt.Errorf("failed to verify message using %s: %w", sig.KeyID, err)
}

actor, err := l.Resolver.ResolveID(r.Context(), keys, sig.KeyID, flags)
actor, err := l.Resolver.ResolveID(r.Context(), l.ActorKeys, sig.KeyID, flags)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch %s: %w", sig.KeyID, err)
}
Expand All @@ -89,7 +89,7 @@ func (l *Listener) verifyRequest(r *http.Request, body []byte, flags ap.Resolver
}
}

actor, err := l.Resolver.ResolveID(r.Context(), keys, sig.KeyID, flags)
actor, err := l.Resolver.ResolveID(r.Context(), l.ActorKeys, sig.KeyID, flags)
if err != nil {
return nil, nil, fmt.Errorf("failed to get key %s to verify message: %w", sig.KeyID, err)
}
Expand Down Expand Up @@ -124,7 +124,7 @@ func (l *Listener) verifyRequest(r *http.Request, body []byte, flags ap.Resolver
return sig, actor, nil
}

func (l *Listener) verifyProof(ctx context.Context, p ap.Proof, activity *ap.Activity, raw []byte, flags ap.ResolverFlag, keys [2]httpsig.Key) (*ap.Actor, error) {
func (l *Listener) verifyProof(ctx context.Context, p ap.Proof, activity *ap.Activity, raw []byte, flags ap.ResolverFlag) (*ap.Actor, error) {
if m := ap.KeyRegex.FindStringSubmatch(p.VerificationMethod); m != nil {
if m2 := ap.GatewayURLRegex.FindStringSubmatch(activity.Actor); m2 != nil {
if m2[1] != m[1] {
Expand All @@ -140,11 +140,11 @@ func (l *Listener) verifyProof(ctx context.Context, p ap.Proof, activity *ap.Act
return nil, fmt.Errorf("failed to verify proof using %s: %w", p.VerificationMethod, err)
}

return l.Resolver.ResolveID(ctx, keys, activity.Actor, flags)
return l.Resolver.ResolveID(ctx, l.ActorKeys, activity.Actor, flags)
}
}

actor, err := l.Resolver.ResolveID(ctx, keys, p.VerificationMethod, flags)
actor, err := l.Resolver.ResolveID(ctx, l.ActorKeys, p.VerificationMethod, flags)
if err != nil {
return nil, fmt.Errorf("failed to get key %s to verify proof: %w", p.VerificationMethod, err)
}
Expand Down
Loading