Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
3890e57
Add Session Pro master key derivation
Doy-lee Jul 31, 2025
5582329
Only give 64b skey from session pro keygen
Doy-lee Aug 1, 2025
f241f86
Apply _hex_u changes to rest of ed25519 tests
Doy-lee Aug 1, 2025
d9b3b09
Use cleared_u32 and 64 from sodium_array header
Doy-lee Aug 1, 2025
d7ba32e
Linting fix
Doy-lee Aug 1, 2025
dd59713
Add basic pro proof verification functions
Doy-lee Jul 31, 2025
d4d6176
Reformat the string split in test_config_userprofile
Doy-lee Aug 4, 2025
af301db
Add pro proof and rotating key pair to user profile
Doy-lee Aug 4, 2025
176d72a
Add c wrappers for Session Pro and proofs
Doy-lee Aug 5, 2025
69f38a7
Update protobufs for Pro and pro proofs
Doy-lee Aug 5, 2025
c397b15
Typedef array u8x32, add it to types.hpp anad use it in new code
Doy-lee Aug 5, 2025
f7047b2
Add Pro message to Content and decrypt it
Doy-lee Aug 6, 2025
f23f718
Update Session protobuf files to session-ios at 084e58f
Doy-lee Aug 8, 2025
6abed5a
Revise the PRO protobuf structures
Doy-lee Aug 8, 2025
fc95375
Port encrypt for namespace into libsession
Doy-lee Aug 8, 2025
af52798
Update decrypt function to return the envelope and the metadata
Doy-lee Aug 8, 2025
ffe0b5b
Add documentation for new session protocol functions
Doy-lee Aug 8, 2025
654b6b6
Add c bindings for new encryption functions
Doy-lee Aug 11, 2025
afb7ad2
Revise docs for session protocol c header
Doy-lee Aug 11, 2025
a1d90a5
Fix incorrect detection of higher char limits and non-optional pro sigs
Doy-lee Aug 12, 2025
c384713
Note that closed groups handles both legacy/non-legacy keys
Doy-lee Aug 12, 2025
9841a4e
Avoid required files in pro messages, handle non-encrypted contents i…
Doy-lee Aug 13, 2025
539f77e
Regen the protobufs w/ non required fields for pro structures
Doy-lee Aug 13, 2025
d58e65a
Allow envelope decrypt to handle encrypted v2 group envelopes
Doy-lee Aug 14, 2025
0bacb89
Do not enforce library types in C interface
Doy-lee Aug 14, 2025
39b8edc
Pro version field should use a key that sorts to the top
Doy-lee Aug 14, 2025
4e2b3d0
Fix C session protocol using std::span and missing comments
Doy-lee Aug 14, 2025
899f2e0
Fixup some comments, add missing x25519 pubkey from C decrypted envelope
Doy-lee Aug 14, 2025
817a102
Linting
Doy-lee Aug 14, 2025
4bc5480
Fix pro tests breaking due to version change to @
Doy-lee Aug 14, 2025
8b5e1fd
Move group msg encryption primitive into session encrypt, avoids circ…
Doy-lee Aug 15, 2025
9bb7d27
Move group encryption into session encrypt
Doy-lee Aug 18, 2025
e36c6e5
Update nomenclamenture, Closed/OpenGroup to Group/Community respectively
Doy-lee Aug 18, 2025
cb207d7
Add some missing typedefs on C structs
Doy-lee Aug 18, 2025
7356825
Document the free-eing requirement on the C interface for session pro…
Doy-lee Aug 18, 2025
68c27ff
We cannot enforce the presence of a pro signature
Doy-lee Aug 18, 2025
ecfc9b7
Don't use assert in test key-gen which breaks in release mode
Doy-lee Aug 19, 2025
a5c4ed8
Add error logging to C apis
Doy-lee Aug 19, 2025
7afeb59
Add TODO on the purpose of PUBKEY in pro_backend
Doy-lee Aug 20, 2025
633b199
Add note that character limit is UTF16 code units for now
Doy-lee Aug 20, 2025
608b6a5
Remove outdated comment, these encrypt dests just receive plaintext
Doy-lee Aug 20, 2025
f7e5a54
Remove the CPP session protocol tests, keep C API
Doy-lee Aug 20, 2025
a068576
Fix groups message not encrypting correctly
Doy-lee Aug 20, 2025
8bfeb88
Remove envelope type, it is inferred from the namespace
Doy-lee Aug 20, 2025
d3ff9a4
Encapsulate the freeing of session protocol encrypt/decrypt funcs
Doy-lee Aug 20, 2025
0b2dbe2
Prohibit the creation of legacy groups messages, no longer supported
Doy-lee Aug 20, 2025
ea411a8
Fix linting
Doy-lee Aug 20, 2025
d5c6e26
Remove the need for ProStatus::Nil by using std::optional
Doy-lee Aug 21, 2025
91d8be7
Add docs to pro config
Doy-lee Aug 21, 2025
6c6d455
Add helper functions for getting PRO_STATUS from proof standalone fro…
Doy-lee Aug 21, 2025
37eb19a
Re-add missing wrapping of 1o1 messages
Doy-lee Aug 25, 2025
2a49789
Mitigate snprintf error prone return value
Doy-lee Aug 25, 2025
680d077
Address some outdated doc comments
Doy-lee Aug 25, 2025
bf5e703
Use bytes32/64/33 in session protocol, add wrapper encrypt for {1o1,g…
Doy-lee Aug 29, 2025
fd5a5dd
Remove destinations that don't encrypt
Doy-lee Aug 29, 2025
8d0948c
Remove namespace from encrypt for destination
Doy-lee Aug 29, 2025
f9ae0d3
Linting
Doy-lee Aug 29, 2025
a0fbd43
Add missing memory header for zstd
Doy-lee Sep 24, 2025
db2e063
Move ProProof into session_protocol.hpp
Doy-lee Sep 24, 2025
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
10 changes: 5 additions & 5 deletions include/session/blinding.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extern "C" {
///
/// Inputs:
/// - `ed25519_seckey` -- [in] the Ed25519 private key of the sender (64 bytes).
/// - `server_pk` -- [in] the public key of the open group server to generate the
/// - `server_pk` -- [in] the public key of the community server to generate the
/// blinded id for (32 bytes).
/// - `blinded_pk_out` -- [out] pointer to a buffer of at least 32 bytes where the blinded_pk will
/// be written if generation was successful.
Expand All @@ -36,7 +36,7 @@ LIBSESSION_EXPORT bool session_blind15_key_pair(
///
/// Inputs:
/// - `ed25519_seckey` -- [in] the Ed25519 private key of the sender (64 bytes).
/// - `server_pk` -- [in] the public key of the open group server to generate the
/// - `server_pk` -- [in] the public key of the community server to generate the
/// blinded id for (32 bytes).
/// - `blinded_pk_out` -- [out] pointer to a buffer of at least 32 bytes where the blinded_pk will
/// be written if generation was successful.
Expand Down Expand Up @@ -75,7 +75,7 @@ LIBSESSION_EXPORT bool session_blind_version_key_pair(
///
/// Inputs:
/// - `ed25519_seckey` -- [in] the Ed25519 private key of the sender (64 bytes).
/// - `server_pk` -- [in] the public key of the open group server to generate the
/// - `server_pk` -- [in] the public key of the community server to generate the
/// blinded id for (32 bytes).
/// - `msg` -- [in] Pointer to a data buffer containing the message to generate a signature for.
/// - `msg_len` -- [in] Length of `msg`
Expand All @@ -97,7 +97,7 @@ LIBSESSION_EXPORT bool session_blind15_sign(
///
/// Inputs:
/// - `ed25519_seckey` -- [in] the Ed25519 private key of the sender (64 bytes).
/// - `server_pk` -- [in] the public key of the open group server to generate the
/// - `server_pk` -- [in] the public key of the community server to generate the
/// blinded id for (32 bytes).
/// - `msg` -- [in] Pointer to a data buffer containing the message to generate a signature for.
/// - `msg_len` -- [in] Length of `msg`
Expand Down Expand Up @@ -145,7 +145,7 @@ LIBSESSION_EXPORT bool session_blind_version_sign(
/// Inputs:
/// - `session_id` -- [in] the session_id to compare (66 bytes with a 05 prefix).
/// - `blinded_id` -- [in] the blinded_id to compare, can be either 15 or 25 blinded (66 bytes).
/// - `server_pk` -- [in] the public key of the open group server to the blinded id came from (64
/// - `server_pk` -- [in] the public key of the community server to the blinded id came from (64
/// bytes).
///
/// Outputs:
Expand Down
2 changes: 1 addition & 1 deletion include/session/config/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ class ConfigBase : public ConfigSig {
/// API: base/ConfigBase::load_key
///
/// Called to load an ed25519 key for encryption; this is meant for use by single-ownership
/// config types, like UserProfile, but not shared config types (closed groups).
/// config types, like UserProfile, but not shared config types (groups).
///
/// Takes a binary string which is either the 32-byte seed, or 64-byte libsodium secret (which
/// is just the seed and pubkey concatenated together), and then calls `key(...)` with the seed.
Expand Down
4 changes: 2 additions & 2 deletions include/session/config/convo_info_volatile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ class val_loader;
/// included, but will be 0 if no messages are read.
/// u - will be present and set to 1 if this conversation is specifically marked unread.
///
/// g - group conversations (aka new, non-legacy closed groups). The key is the group identifier
/// g - group conversations (aka new, non-legacy groups). The key is the group identifier
/// (beginning with 03). Values are dicts with keys:
/// r - the unix timestamp (in integer milliseconds) of the last-read message. Always
/// included, but will be 0 if no messages are read.
/// u - will be present and set to 1 if this conversation is specifically marked unread.
///
/// C - legacy group conversations (aka closed groups). The key is the group identifier (which
/// C - legacy group conversations (aka groups). The key is the group identifier (which
/// looks indistinguishable from a Session ID, but isn't really a proper Session ID). Values
/// are dicts with keys:
/// r - the unix timestamp (integer milliseconds) of the last-read message. Always included,
Expand Down
6 changes: 3 additions & 3 deletions include/session/config/groups/info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ class Info : public ConfigBase {
/// API: groups/Info::set_delete_before
///
/// Sets a "delete before" unix timestamp: this instructs clients to delete all messages from
/// the closed group history with a timestamp earlier than this value. Returns nullopt if no
/// the group history with a timestamp earlier than this value. Returns nullopt if no
/// delete-before timestamp is set.
///
/// The given value is checked for sanity (e.g. if you pass milliseconds it will be
Expand All @@ -250,7 +250,7 @@ class Info : public ConfigBase {
/// API: groups/Info::get_delete_before
///
/// Returns the delete-before unix timestamp (seconds) for the group; clients should delete all
/// messages from the closed group with timestamps earlier than this value, if set.
/// messages from the group with timestamps earlier than this value, if set.
///
/// Returns std::nullopt if no delete-before timestamp is set.
///
Expand Down Expand Up @@ -279,7 +279,7 @@ class Info : public ConfigBase {
/// API: groups/Info::get_delete_attach_before
///
/// Returns the delete-attachments-before unix timestamp (seconds) for the group; clients should
/// delete all messages from the closed group with timestamps earlier than this value, if set.
/// delete all messages from the group with timestamps earlier than this value, if set.
///
/// Returns std::nullopt if no delete-attachments-before timestamp is set.
///
Expand Down
13 changes: 13 additions & 0 deletions include/session/config/groups/keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
extern "C" {
#endif

#include "../../types.h"
#include "../base.h"
#include "../util.h"

Expand Down Expand Up @@ -132,6 +133,18 @@ LIBSESSION_EXPORT size_t groups_keys_size(const config_group_keys* conf);
/// - `const unsigned char*` -- pointer to the 32-byte key, or nullptr if there
LIBSESSION_EXPORT const unsigned char* groups_keys_get_key(const config_group_keys* conf, size_t N);

/// API: groups/groups_keys_group_enc_key
///
/// Accesses the current encryption key: that is, the most current group decryption key. Returns the
/// 32 byte private key, or, an empty span if there are no encryption keys at all.
///
/// Inputs:
/// - `conf` -- the groups config object
///
/// Outputs:
/// - `true` if we have admin keys, `false` otherwise.
LIBSESSION_EXPORT const span_u8 groups_keys_group_enc_key(const config_group_keys* conf);

/// API: groups/groups_keys_is_admin
///
/// Returns true if this object has the group private keys, i.e. the user is an all-powerful
Expand Down
50 changes: 9 additions & 41 deletions include/session/config/groups/keys.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -664,46 +664,12 @@ class Keys : public ConfigSig {

/// API: groups/Keys::encrypt_message
///
/// Compresses, signs, and encrypts group message content.
///
/// This method is passed a binary value containing a group message (typically a serialized
/// protobuf, but this method doesn't care about the specific data). That data will be, in
/// order:
/// - compressed (but only if this actually reduces the data size)
/// - signed by the user's underlying session Ed25519 pubkey
/// - tagged with the user's underlying session Ed25519 pubkey (from which the session id can be
/// computed).
/// - all of the above encoded into a bt-encoded dict
/// - suffix-padded with null bytes so that the final output value will be a multiple of 256
/// bytes
/// - encrypted with the most-current group encryption key
///
/// Since compression and padding is applied as part of this method, it is not required that the
/// given message include its own padding (and in fact, such padding will typically be
/// compressed down to nothing (if non-random)).
///
/// This final encrypted value is then returned to be pushed to the swarm as-is (i.e. not
/// further wrapped). For users downloading the message, all of the above is processed in
/// reverse by passing the returned message into `decrypt_message()`.
///
/// The current implementation uses XChaCha20-Poly1305 for encryption and zstd for compression;
/// the bt-encoded value is a dict consisting of keys:
/// - "": the version of this encoding, currently set to 1. This *MUST* be bumped if this is
/// changed in such a way that older clients will not be able to properly decrypt such a
/// message.
/// - "a": the *Ed25519* pubkey (32 bytes) of the author of the message. (This will be
/// converted to a x25519 pubkey to extract the sender's session id when decrypting).
/// - "s": signature by "a" of whichever of "d" or "z" are included in the data.
/// Exacly one of:
/// - "d": the uncompressed data (which must be non-empty if present)
/// - "z": the zstd-compressed data (which must be non-empty if present)
///
/// When compression is enabled (by omitting the `compress` argument or specifying it as true)
/// then ZSTD compression will be *attempted* on the plaintext message and will be used if the
/// compressed data is smaller than the uncompressed data. If disabled, or if compression does
/// not reduce the size, then the message will not be compressed.
///
/// This method will throw on failure, which can happen in two cases:
/// Compresses, signs, and encrypts group message content with the user's underlying session
/// Ed25519 pubkey and the most-current group encryption key.
///
/// See: crypto/encrypt_for_group
///
/// This method will throw on failure:
/// - if there no encryption keys are available at all (which should not occur in normal use).
/// - if given a plaintext buffer larger than 1MB (even if the compressed version would be much
/// smaller). It is recommended that clients impose their own limits much smaller than this
Expand All @@ -728,7 +694,9 @@ class Keys : public ConfigSig {

/// API: groups/Keys::decrypt_message
///
/// Decrypts group message content that was presumably encrypted with `encrypt_message`,
/// Decrypts group message content that encrypted with `encrypt_message`.
///
/// See: crypto/decrypt_group_message
/// verifies the sender signature, decompresses the message (if necessary) and then returns the
/// author pubkey and the plaintext data.
///
Expand Down
34 changes: 34 additions & 0 deletions include/session/config/namespaces.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

typedef enum NAMESPACE {
// Messages sent to an updated group which should be able to be retrieved by revoked
// members are stored in this namespace
NAMESPACE_REVOKED_RETRIEVABLE_GROUP_MESSAGES = -11,

// Messages sent to one-to-one conversations are stored in this namespace
NAMESPACE_DEFAULT = 0,
NAMESPACE_USER_PROFILE = 2,
NAMESPACE_CONTACTS = 3,
NAMESPACE_CONVO_INFO_VOLATILE = 4,
NAMESPACE_USER_GROUPS = 5,

// Messages sent to a group:
NAMESPACE_GROUP_MESSAGES = 11,
// Groups config namespaces (i.e. for shared config of the group itself, not one user's group
// settings)
NAMESPACE_GROUP_KEYS = 12,
NAMESPACE_GROUP_INFO = 13,
NAMESPACE_GROUP_MEMBERS = 14,

// The local config should never be pushed but this gives us a nice identifier for each config
// type
NAMESPACE_LOCAL = 9999,
} NAMESPACE;

#ifdef __cplusplus
} // extern "C"
#endif
26 changes: 16 additions & 10 deletions include/session/config/namespaces.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,31 @@

#include <cstdint>

#include "namespaces.h"

namespace session::config {

enum class Namespace : std::int16_t {
UserProfile = 2,
Contacts = 3,
ConvoInfoVolatile = 4,
UserGroups = 5,
RevokedRetrievableGroupMessages = NAMESPACE_REVOKED_RETRIEVABLE_GROUP_MESSAGES,

// Messages sent to one-to-one conversations are stored in this namespace
Default = NAMESPACE_DEFAULT,
UserProfile = NAMESPACE_USER_PROFILE,
Contacts = NAMESPACE_CONTACTS,
ConvoInfoVolatile = NAMESPACE_CONVO_INFO_VOLATILE,
UserGroups = NAMESPACE_USER_GROUPS,

// Messages sent to a closed group:
GroupMessages = 11,
// Messages sent to a group:
GroupMessages = NAMESPACE_GROUP_MESSAGES,
// Groups config namespaces (i.e. for shared config of the group itself, not one user's group
// settings)
GroupKeys = 12,
GroupInfo = 13,
GroupMembers = 14,
GroupKeys = NAMESPACE_GROUP_KEYS,
GroupInfo = NAMESPACE_GROUP_INFO,
GroupMembers = NAMESPACE_GROUP_MEMBERS,

// The local config should never be pushed but this gives us a nice identifier for each config
// type
Local = 9999,
Local = NAMESPACE_LOCAL,
};

} // namespace session::config
38 changes: 38 additions & 0 deletions include/session/config/pro.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "../export.h"
#include "session/session_protocol.h"

typedef struct pro_config {
uint8_t rotating_privkey[64];
pro_proof proof;
} pro_pro_config;

/// API: pro/pro_verify
///
/// Verify the proof was signed by the `verify_pubkey` and that the `rotating_privkey` in the `pro`
/// config rederives to the `rotating_pubkey` embedded in the proof.
///
/// Inputs:
/// - `proof` -- Proof to verify
/// - `verify_pubkey` -- Array of bytes containing the public key to (typically the Session Pro
/// Backend public key) verify the proof against.
/// - `verify_pubkey_len` -- Length of the `verify_pubkey` this must be 32 bytes, but is
/// parameterised to detect errors about incorrectly sized arrays by the caller.
///
/// Outputs:
/// - `bytes32` -- The 32 byte hash calculated from the proof
LIBSESSION_EXPORT bool pro_config_verify_signature(
pro_config const* pro, uint8_t const* verify_pubkey, size_t verify_pubkey_len);

#ifdef __cplusplus
} // extern "C"
#endif
46 changes: 46 additions & 0 deletions include/session/config/pro.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <session/config.hpp>
#include <session/config/base.hpp>
#include <session/session_protocol.hpp>

namespace session::config {

/// keys used currently or in the past (so that we don't reuse):
///
/// p + pro data
/// |
/// +-- @ - version
/// +-- g - gen_index_hash
/// +-- r - rotating ed25519 pubkey
/// +-- e - expiry unix timestamp (in seconds)
/// +-- s - proof signature, signed by the Session Pro Backend's ed25519 key
/// +-- r - rotating ed25519 privkey
/// +-- p - proof
class ProConfig {
public:
/// Private key for the public key key specified in the proof. This is synced between clients
/// to allow multiple clients to synchronise the Session Pro Proof and also the keys necessary
/// to use the proof.
cleared_uc64 rotating_privkey;

/// A cryptographic proof for entitling an Ed25519 key to Session Pro
ProProof proof;

/// API: pro/Pro::verify
///
/// Verify the proof and that the proof's rotating public key matches the public key of the
/// `rotating_privkey`
///
/// Inputs:
/// - `verify_pubkey` -- Ed25519 public key of the corresponding secret key to check if they are
/// the original signatory of the proof.
///
/// Outputs:
/// - `bool` - True if the proof was verified and the proof's rotating public key corresponds to
/// the public component of the `rotating_privkey`.
bool verify_signature(const array_uc32& verify_pubkey) const;

bool load(const dict& root);
};
}; // namespace session::config
10 changes: 5 additions & 5 deletions include/session/config/user_groups.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,16 @@ struct base_group_info {
notify_mode notifications = notify_mode::defaulted; // When the user wants notifications
int64_t mute_until = 0; // unix timestamp (seconds) until which notifications are disabled

std::string name; // human-readable; always set for a legacy closed group, only used before
// joining a new closed group (after joining the group info provide the name)
std::string name; // human-readable; always set for a legacy group, only used before
// joining a new group (after joining the group info provide the name)

bool invited = false; // True if this is currently in the invite-but-not-accepted state.

protected:
void load(const dict& info_dict);
};

/// Struct containing legacy group info (aka "closed groups").
/// Struct containing legacy group info (aka "groups").
struct legacy_group_info : base_group_info {
std::string session_id; // The legacy group "session id" (33 bytes).
std::vector<unsigned char> enc_pubkey; // bytes (32 or empty)
Expand Down Expand Up @@ -185,7 +185,7 @@ struct legacy_group_info : base_group_info {

constexpr int NOT_REMOVED = 0, KICKED_FROM_GROUP = 1, GROUP_DESTROYED = 2;

/// Struct containing new group info (aka "closed groups v2").
/// Struct containing new group info (aka "groups v2").
struct group_info : base_group_info {
std::string id; // The group pubkey (66 hex digits); this is an ed25519 key, prefixed with "03"
// (to distinguish it from a 05 x25519 pubkey session id).
Expand Down Expand Up @@ -344,7 +344,7 @@ class UserGroups : public ConfigBase {

/// API: user_groups/UserGroups::get_group
///
/// Looks up and returns a group (aka new closed group) by group ID (hex, looks like a Session
/// Looks up and returns a group (aka new group) by group ID (hex, looks like a Session
/// ID but starting with 03). Returns nullopt if the group was not found, otherwise returns a
/// filled out `group_info`.
///
Expand Down
Loading