From encryptEvent, the group-session algorithm this build used.
From decryptEvent, spec section 7.1: this library decrypts events, it
does not authenticate their senders. This value is read from the
incoming event, not independently verified, and is unauthenticated
transport metadata — treat it accordingly, not as a claim this
library has confirmed. This carried a milestone twice, and both have
now passed without the property changing: cross-signing has landed and
this value is still read from the incoming event and never re-derived.
It is not scoped to a milestone at all, so there is no version to wait
for. What cross-signing adds is EventEnvelope.senderVerification,
a separate value, and not a promotion of this one.
Do not trust this field's name on the decrypt path.
From encryptEvent, this is the wire ciphertext: send it as the
content of your m.room.encrypted event.
From decryptEvent, this is the plaintext that call just
recovered. One type describes both directions, so the name comes from
the direction that produced it first and is wrong for the other. There
is no second field to read instead.
The consequence is a handling rule, not a naming quibble. Everything a
product does to plaintext, it must do to this value on the decrypt
path: do not log it, do not persist it unencrypted, do not put it in a
crash report or an analytics event, and do not let it into a console
statement written while debugging. The Rust core hand-writes a
redacting Debug for exactly this reason, and it cannot reach across
this boundary to do the same for JavaScript.
Fully qualified @user:server, verbatim. Spec section 10.
From encryptEvent, this device's own identity — authenticated by
definition.
From decryptEvent, spec section 7.1: this library decrypts events, it
does not authenticate their senders. This is the sender the
homeserver delivered on the outer, not-yet-decrypted event, not a
value this library independently confirmed, and it is
unauthenticated transport metadata. Verifying the sending device
does not change that, by string or by scanned code; cross-signing is
what would, and it has landed without moving this field, which is the
point senderVerification forty lines below makes at length. This said
"and it is M4", naming a milestone as a thing still to come. A
product that reads it as the cryptographic sender of a successfully
decrypted event has assumed something this library does not provide,
and that
assumption is the shape impersonation takes. Read it together with
senderVerification below, which is what says how much of a claim it
is -- and note that 'mismatched_sender' is exactly the case where
this string is a lie decryption did not catch.
OptionalsenderWhat this library knew about the sender of this event at the moment it decrypted it -- see SenderVerification, including which of its values cannot arrive here yet and why completing a verification does not change this one. That said "which three of its values" until M4, and the count is the kind of detail that goes stale in silence, so there is no count here now.
Present on every successful decryptEvent. Absent from
encryptEvent. The same one-type-two-directions caveat algorithm
and sender above each carry, in its strongest form: those two hold a
real value in both directions, and this one is discarded on the
encrypt path rather than missing from it. The layer underneath does
receive a value when it encrypts, and that value is 'verified' --
upstream reporting on this device's own keys, which is a statement
about a device and is true of a machine that has never verified
anything. It is dropped rather than forwarded onto the field a
decrypted event reads. This used to add "the one word this release
cannot honestly attach to an event", which stopped being true when the
core learned to cross-sign; the asymmetry it was pointing at is real
and survives the correction. On the encrypt path the word costs
nothing and means nothing. On the decrypt path it costs the whole
seven-step chain, which is why forwarding the free one would be a
fabrication rather than a shortcut. Absent here means "this question
was not asked of this event".
It is a snapshot, and it can go stale. Upstream defines it as the
state of the sending device at the time of decryption: it "may change
in the future if a device gets verified or deleted", and callers who
persist it are told to mark it dirty when a device change is received
down the sync. That obligation is yours once you hold this value. The
trigger is already in your hands -- device_lists.changed on the
/sync response you pass to receiveSyncChanges -- and nothing
in this library re-derives a stored value for you. A record that looks
static is not the same as a fact that stays true.
Going stale is the only direction it moves. It does not improve.
Verifying someone changes what their next messages say, not what
their old ones said. The value belongs to the session the event was
encrypted with, it is computed once when that session's key arrives,
and nothing recomputes it for a session whose sender was already
identified: so a message decrypted while its sender was merely
cross-signed keeps reading 'unverified_identity' until that session
is replaced, however thoroughly you verify them afterwards. Design a
badge that says "from here on" rather than one that backfills a
conversation, because the backfill will not arrive. Asserted end to
end, not inferred: the core's
tests/verified_sender.rs::history_does_not_improve_when_the_sender_is_verified_later
decrypts one event before a full verification and the same event after
it, and then a message on a session created afterwards to show that
the verification really did take effect.
Typed envelope for an encrypted or decrypted event.