telegabber/e2ee/policy.go
2026-07-30 00:09:22 -04:00

83 lines
2.8 KiB
Go

package e2ee
import "fmt"
// Mode is the account-wide OMEMO policy (see persistence.Session.OMEMO),
// combined with a chat's optional Override and its auto-upgrade sticky
// flag (Backend.Enabled) by ShouldEncrypt to decide whether to attempt
// outgoing encryption for that chat right now.
type Mode int
const (
// ModeAuto (the default) only encrypts once a chat has auto-upgraded
// (Backend.Enabled), i.e. its peer has already sent this chat an OMEMO
// message. This is the original, only behavior before Mode existed.
ModeAuto Mode = iota
// ModeOn always attempts to encrypt, regardless of whether the peer
// has ever shown OMEMO support. If no session can be established, the
// send fails outright rather than falling back to plaintext - the
// same OMEMOSendFailedBody convention already used at every Encrypt
// call site applies, it's just reached unconditionally now instead of
// only for already-active chats. Incoming plaintext is still accepted
// and processed as usual - this mode only ever affects outgoing.
ModeOn
// ModeOff never encrypts outgoing content, even for a chat that has
// already auto-upgraded - inbound OMEMO messages are still decrypted
// normally regardless of this mode (decryption never consults Mode at
// all, only the encrypt hooks do).
ModeOff
)
func (m Mode) String() string {
switch m {
case ModeOn:
return "on"
case ModeOff:
return "off"
default:
return "auto"
}
}
// ParseMode maps persistence.Session.OMEMO's stored string to a Mode - an
// empty string (an account that predates this field, or never touched it)
// is treated the same as "auto", matching Mode's zero value and default.
func ParseMode(s string) (Mode, error) {
switch s {
case "", "auto":
return ModeAuto, nil
case "on":
return ModeOn, nil
case "off":
return ModeOff, nil
default:
return ModeAuto, fmt.Errorf("e2ee: unknown mode %q (want on, auto, or off)", s)
}
}
// ShouldEncrypt decides whether owner's outgoing content should be
// encrypted right now, combining (in priority order): owner's explicit
// per-chat Override, if one has been set; otherwise mode's account-wide
// policy, consulting backend's auto-upgrade sticky flag (Enabled) only for
// ModeAuto. This is the single place that combination logic lives - every
// encrypt hook (SendMessageToGateway, sendMessagesReverse,
// updateMessageContent) calls this instead of checking Enabled directly,
// so a chat's effective behavior can never drift between call sites.
func ShouldEncrypt(backend Backend, owner PeerID, mode Mode) (bool, error) {
if override, isSet, err := backend.Override(owner); err != nil {
return false, err
} else if isSet {
return override, nil
}
switch mode {
case ModeOn:
return true, nil
case ModeOff:
return false, nil
default:
return backend.Enabled(owner)
}
}