Something went wrong. Try again.
A community based topic aggregation platform built on atproto
Something went wrong. Try again.
4.1 kB · 112 lines
Go
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113package moderation
import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/binary" "fmt" "hash" "time")
const ( actionCursorVersion = byte(1) actionCursorPublic = byte(0) actionCursorAdmin = byte(1) // actionCursorHeader is the version byte, the context byte and the // big-endian createdAt microseconds that precede the action ID. actionCursorHeader = 1 + 1 + 8 // actionCursorLabel separates action cursor MACs from every other use of // the cursor secret. actionCursorLabel = "social.coves.moderation.actionCursor\x00")
// actionCursor is a structurally valid cursor whose signature is not yet// verified: the signature binds the filter digest, which is recomputed only// after the filter targets resolve.type actionCursor struct { payload []byte signature []byte key ActionKey}
func actionCursorContext(admin bool) byte { if admin { return actionCursorAdmin } return actionCursorPublic}
func writeActionFilter(hasher hash.Hash, value string) { var length [8]byte binary.BigEndian.PutUint64(length[:], uint64(len(value))) _, _ = hasher.Write(length[:]) _, _ = hasher.Write([]byte(value))}
func (s *service) actionFilterDigest(params ListActionsParams, actionID string, query ActionListQuery) [sha256.Size]byte { hasher := sha256.New() for _, value := range []string{ s.config.InstanceDID, params.Subject, params.Collection, params.Action, params.Origin, params.Authority, params.Actor, params.Community, params.Since, params.Until, actionID, query.AuthorityDID, query.ActorDID, query.CommunityDID, } { writeActionFilter(hasher, value) } var digest [sha256.Size]byte copy(digest[:], hasher.Sum(nil)) return digest}
// actionCursorMAC signs a cursor payload (version, context, createdAt, action// ID) together with the filter digest, which the cursor never carries.func (s *service) actionCursorMAC(payload []byte, digest [sha256.Size]byte) []byte { mac := hmac.New(sha256.New, []byte(s.config.CursorSecret)) _, _ = mac.Write([]byte(actionCursorLabel)) _, _ = mac.Write(payload[:2]) _, _ = mac.Write(digest[:]) _, _ = mac.Write(payload[2:]) return mac.Sum(nil)}
func (s *service) signActionCursor(admin bool, digest [sha256.Size]byte, last ActionKey) string { payload := make([]byte, actionCursorHeader, actionCursorHeader+len(last.ID)+sha256.Size) payload[0] = actionCursorVersion payload[1] = actionCursorContext(admin) binary.BigEndian.PutUint64(payload[2:actionCursorHeader], uint64(last.CreatedAt.UnixMicro())) payload = append(payload, last.ID...) payload = append(payload, s.actionCursorMAC(payload, digest)...) return base64.RawURLEncoding.EncodeToString(payload)}
// decodeActionCursor checks a cursor's encoding, length, version and context.// Its signature is checked later by verifyActionCursor.func decodeActionCursor(encoded string, admin bool) (*actionCursor, error) { decoded, err := base64.RawURLEncoding.Strict().DecodeString(encoded) if err != nil || len(decoded) <= actionCursorHeader+sha256.Size || len(decoded) > actionCursorHeader+maxActionIDLength+sha256.Size || decoded[0] != actionCursorVersion || decoded[1] != actionCursorContext(admin) { return nil, fmt.Errorf("%w: malformed action cursor", ErrInvalidCursor) } payload := decoded[:len(decoded)-sha256.Size] return &actionCursor{ payload: payload, signature: decoded[len(payload):], key: ActionKey{ CreatedAt: time.UnixMicro(int64(binary.BigEndian.Uint64(payload[2:actionCursorHeader]))).UTC(), ID: string(payload[actionCursorHeader:]), }, }, nil}
// verifyActionCursor rejects a cursor not signed for this request's filter// digest: a forged cursor, or one replayed with changed filters or with// targets that now resolve to different DIDs.func (s *service) verifyActionCursor(cursor *actionCursor, digest [sha256.Size]byte) error { if !hmac.Equal(cursor.signature, s.actionCursorMAC(cursor.payload, digest)) { return fmt.Errorf("%w: action cursor signature does not match the filters and resolved targets", ErrInvalidCursor) } return nil}