- arrayref can be replaced by split_first_chunk -- not quite as
concisely, but actually a little safer, and no macros involved
- array-concat has an equivalent in const_str
- num_enum has already been replaced by derive_more
- The test using regex has been removed
- Drop X3DH tests
- Drop cross-version tests with libsignal v0.12 and v0.21
- Change session benchmarks to use PQXDH, which is relevant if doing
comparisons before/after this commit.
This PR integrates a post-quantum ratchet (SPQR) into libsignal, using an API that maintains its own internal chain and provides per-message keys. In doing so, it also aims to be fully backwards-compatible with current clients and stored session state.
## Backwards compatibility with current clients
Remote clients that connect to us or that we connect to may not have this integration. If they don't, their SignalMessage wire format should still deserialize, and in doing so we'll receive an empty pq_ratchet field. SQPR handles this internally, by downgrading the protocol version to "version 0" or "don't do anything". Note that should we eventually want to disallow this, we can do so via increasing the `min_version` field passed into the SQPR init functions to V1. This is also the method by which we would upgrade SQPR from v1 to a future v2, etc.
## Opt-in
The publicly facing API calls for this now expose an explicit opt-in via a passed-in `use_pq_ratchet` bool (and associated enums in language-specific APIs). If false, they default to SQPR `v0`, IE: none. If true, they try to set up SPQR on new sessions, but will downgrade if the remote party cannot or will not do the same.
This fixes a bug introduced by cd36118 where starting a new session
locally would prevent incoming messages on a previous session from
being decrypted if that session hadn't advanced past the "pre-key"
stage. Fix this by promoting the session *before* successful
decryption instead of after; since we won't *save* the promotion
unless the message decrypts successfully, there's ultimately no change
in either the failure or success cases *except* when hitting this bug.
This is necessarily a breaking change because the EC key operations
can no longer return SignalProtocolError. However, the types are still
exported through libsignal_protocol, and SignalProtocolError
implements From<CurveError>, so hopefully any clients will be guided
to update to the new usage.
Other changes:
- PublicKey::public_key_bytes no longer uses Result; any future curve
types will also need to keep their public key in a borrowable form.
- PublicKey::verify_signature also no longer uses Result; if a future
curve type does not support verifying a signature, or if the
signature is structurally invalid, that should be treated the same
as a verification failure. (This is already how structurally invalid
Curve25519 signatures were being treated.)
This change allows other crates to depend on the smaller
libsignal_core rather than all of libsignal_protocol (though it also
means libsignal_core is no longer as small as it used to be).
`cargo update` performed with Cargo 1.72 to avoid advancing our MSRV. assert_cmd, clap, protobuf, and protobuf-json-mapping needed to be manually held back.
Plus, explicit bumps for
- env_logger 0.11
- heck 0.5
- itertools 0.13
- num_enum 0.7
- prost 0.13
- tungstenite 0.23
And disallowing downgrading curve25519-dalek below the security update in 4.1.3.
Use Result to signal success or failure instead of Option::Some
signalling an error. This makes it easy to use combinators like
Result::map_err to more succinctly express the same operations.
Introduce a helper for SignalProtocolError to more succinctly construct
ApplicationCallbackError instances.
Previously we had the feature off but listed the dependency on
pqcrypto-kyber as non-optional, which was wasted work.
Note that the two versions of pqcrypto-kyber don't actually coexist
today! This should be treated as an API proof-of-concept, much like
our Kyber768 wrapper.
...as well as related types Aci, Pni, ServiceId,
ServiceIdFixedWidthBinaryBytes, ServiceIdKind, and DeviceId.
...so that zkgroup and libsignal-net don't have to depend on
libsignal-protocol (and indirectly on Kyber).
The types are still exported from libsignal-protocol, so this is not a
source-breaking change.
ProtocolAddress is still defined as a (String, DeviceId) pair; a
switch to (ServiceId, DeviceId) will probably still happen in the
future, but not in this commit.
This uses the Rayon library to perform a MapReduce-like operation of
computing key material on recipients and folding them together into
intermediate buffers, with one final collection step at the end. As
written this uses Rayon's default thread pool, which will be lazily
initialized with one worker thread per logical core. We're not trying
to share thread pools with either libsignal-net's tokio contexts,
RingRTC's dedicated threads, or a platform-specific work queue like
iOS's Dispatch; let's keep things simple for now.
As a downside, the code now has to fetch all of the recipients'
identity keys up front, since it's not guaranteed that loading from
the IdentityKeyStore is thread-safe. However, the significant
improvement in wall time spent generating key material for large
recipient lists on even a dual-core system makes this worth it.