Skip to content

Commit d509d1d

Browse files
committed
Remove the unix_ts_ms arg from decoding, use the sent timestamp internally
1 parent dc3fc6b commit d509d1d

File tree

4 files changed

+64
-28
lines changed

4 files changed

+64
-28
lines changed

include/session/session_protocol.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,8 @@ session_protocol_pro_features_for_msg session_protocol_pro_features_for_utf16(
458458
/// - `ed25519_privkey` -- The sender's libsodium-style secret key (64 bytes). Can also be passed as
459459
/// a 32-byte seed. Used to encrypt the plaintext.
460460
/// - `ed25519_privkey_len` -- The length of the ed25519_privkey buffer in bytes (32 or 64).
461-
/// - `sent_timestamp_ms` -- The timestamp to assign to the message envelope, in milliseconds.
461+
/// - `sent_timestamp_ms` -- The timestamp to assign to the message envelope, in milliseconds. This
462+
/// should match the protobuf encoded Content's `sigtimestamp` in the given `plaintext`.
462463
/// - `pro_rotating_ed25519_privkey` -- Optional rotating Session Pro Ed25519 key (64-bytes or
463464
/// 32-byte seed) to sign the encoded content if you wish to entitle the message to Session Pro.
464465
/// If provided, the corresponding proof must be set in the `Content`. The signature must not be
@@ -747,9 +748,6 @@ LIBSESSION_EXPORT void session_protocol_encode_for_destination_free(
747748
/// caller is done with the result.
748749
///
749750
/// Inputs:
750-
/// - `unix_ts_ms` -- pass in the current system time which is used to determine, whether or
751-
/// not the Session Pro proof has expired or not if it is in the payload. Ignored if there's no
752-
/// proof in the message.
753751
/// - `pro_backend_pubkey` -- the Session Pro backend public key to verify the signature embedded in
754752
/// the proof, validating whether or not the attached proof was indeed issued by an authorised
755753
/// issuer. Ignored if there's no proof in the message.
@@ -800,7 +798,6 @@ session_protocol_decoded_envelope session_protocol_decode_envelope(
800798
const session_protocol_decode_envelope_keys* keys,
801799
const void* envelope_plaintext,
802800
size_t envelope_plaintext_len,
803-
uint64_t unix_ts_ms,
804801
OPTIONAL const void* pro_backend_pubkey,
805802
size_t pro_backend_pubkey_len,
806803
OPTIONAL char* error,

include/session/session_protocol.hpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,8 @@ std::vector<uint8_t> pad_message(std::span<const uint8_t> payload);
397397
/// not be already encrypted and must not be padded.
398398
/// - ed25519_privkey -- The sender's libsodium-style secret key (64 bytes). Can also be passed as
399399
/// a 32-byte seed. Used to encrypt the plaintext.
400-
/// - sent_timestamp -- The timestamp to assign to the message envelope, in milliseconds.
400+
/// - sent_timestamp -- The timestamp to assign to the message envelope, in milliseconds. This
401+
/// should match the protobuf encoded Content's `sigtimestamp` in the given `plaintext`.
401402
/// - recipient_pubkey -- The recipient's Session public key (33 bytes).
402403
/// - pro_rotating_ed25519_privkey -- Optional libsodium-style secret key (64 bytes) that is the
403404
/// secret component of the user's Session Pro Proof `rotating_pubkey`. This key is authorised to
@@ -558,6 +559,12 @@ std::vector<uint8_t> encode_for_destination(
558559
/// the pro fields will be populated with data about the Session Pro proof embedded in the envelope
559560
/// including the features used and if the proof was valid/expired e.t.c.
560561
///
562+
/// The sent timestamp of the protobuf encoded content is the timestamp that the Session Pro proof
563+
/// is checked against to ensure that it was not expired at the time the message was constructed.
564+
/// Once a proof is decoded successfully and was not deemed expired (e.g. pro status returned
565+
/// success) any additional timestamp can be checked against the proof by comparing the timestamp on
566+
/// the proof itself directly (since the proof has been cryptographically verified at that point).
567+
///
561568
/// This function will throw if parsing failed such as a required field is missing, the field is
562569
/// smaller or larger than expected, decryption failed, or an invariant failed. Notably this
563570
/// function does not throw if the Session Pro proof failed to verify. Always check the pro status
@@ -574,9 +581,6 @@ std::vector<uint8_t> encode_for_destination(
574581
///
575582
/// - `envelope_payload` -- the envelope payload either encrypted (groups v2 style) or unencrypted
576583
/// (1o1 or legacy groups).
577-
/// - `unix_ts` -- pass in the current system time which is used to determine, whether or
578-
/// not the Session Pro proof has expired or not if it is in the payload. Ignored if there's no
579-
/// proof in the message.
580584
/// - `pro_backend_pubkey` -- the Session Pro backend public key to verify the signature embedded in
581585
/// the proof, validating whether or not the attached proof was indeed issued by an authorised
582586
/// issuer
@@ -600,7 +604,6 @@ std::vector<uint8_t> encode_for_destination(
600604
DecodedEnvelope decode_envelope(
601605
const DecodeEnvelopeKey& keys,
602606
std::span<const uint8_t> envelope_payload,
603-
std::chrono::sys_time<std::chrono::milliseconds> unix_ts,
604607
const array_uc32& pro_backend_pubkey);
605608

606609
/// API: session_protocol/decode_for_community

src/session_protocol.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,6 @@ std::vector<uint8_t> encode_for_destination(
649649
DecodedEnvelope decode_envelope(
650650
const DecodeEnvelopeKey& keys,
651651
std::span<const uint8_t> envelope_payload,
652-
std::chrono::sys_time<std::chrono::milliseconds> unix_ts,
653652
const array_uc32& pro_backend_pubkey) {
654653
DecodedEnvelope result = {};
655654
SessionProtos::Envelope envelope = {};
@@ -819,7 +818,7 @@ DecodedEnvelope decode_envelope(
819818
SessionProtos::Content content = {};
820819
if (!content.ParseFromArray(result.content_plaintext.data(), result.content_plaintext.size()))
821820
throw std::runtime_error{fmt::format(
822-
"Parse content from envelope failed: {}", result.content_plaintext.size())};
821+
"Parse content from envelope failed: {}b", result.content_plaintext.size())};
823822

824823
// A signature must always be present on the envelope. This is to make a pro and non-pro
825824
// envelope indistinguishable. If the message does not have pro then this signature must still
@@ -842,6 +841,12 @@ DecodedEnvelope decode_envelope(
842841
std::memcpy(result.envelope.pro_sig.data(), pro_sig.data(), pro_sig.size());
843842

844843
if (content.has_promessage()) {
844+
if (!content.sigtimestamp())
845+
throw std::runtime_error{fmt::format(
846+
"Content does not have signature timestamp set, pro proof expiry is "
847+
"unverifiable (content was {}b)",
848+
result.content_plaintext.size())};
849+
845850
// Mark the envelope as having a pro signature that the caller can use.
846851
result.envelope.flags |= SESSION_PROTOCOL_ENVELOPE_FLAGS_PRO_SIG;
847852
DecodedPro& pro = result.pro.emplace();
@@ -891,6 +896,8 @@ DecodedEnvelope decode_envelope(
891896

892897
// Note that we sign the envelope content wholesale. For 1o1 which are padded to 160
893898
// bytes, this means that we expected the user to have signed the padding as well.
899+
auto unix_ts = std::chrono::sys_time<std::chrono::milliseconds>(
900+
std::chrono::milliseconds(content.sigtimestamp()));
894901
signed_msg.msg = to_span(envelope.content());
895902
pro.status = proof.status(pro_backend_pubkey, unix_ts, signed_msg);
896903
}
@@ -1428,7 +1435,6 @@ session_protocol_decoded_envelope session_protocol_decode_envelope(
14281435
const session_protocol_decode_envelope_keys* keys,
14291436
const void* envelope_plaintext,
14301437
size_t envelope_plaintext_len,
1431-
uint64_t unix_ts_ms,
14321438
const void* pro_backend_pubkey,
14331439
size_t pro_backend_pubkey_len,
14341440
char* error,
@@ -1465,8 +1471,6 @@ session_protocol_decoded_envelope session_protocol_decode_envelope(
14651471
result_cpp = decode_envelope(
14661472
keys_cpp,
14671473
{static_cast<const uint8_t*>(envelope_plaintext), envelope_plaintext_len},
1468-
std::chrono::sys_time<std::chrono::milliseconds>(
1469-
std::chrono::milliseconds(unix_ts_ms)),
14701474
pro_backend_pubkey_cpp.data);
14711475
result.success = true;
14721476
break;

tests/test_session_protocol.cpp

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,18 @@ static SerialisedProtobufContentWithProForTesting build_protobuf_content_with_se
2828
std::string_view data_body,
2929
const array_uc64& user_rotating_privkey,
3030
const array_uc64& pro_backend_privkey,
31+
std::chrono::sys_seconds content_unix_ts,
3132
std::chrono::sys_seconds pro_expiry_unix_ts,
3233
session_protocol_pro_message_bitset msg_bitset,
3334
session_protocol_pro_profile_bitset profile_bitset) {
3435
SerialisedProtobufContentWithProForTesting result = {};
3536

3637
// Create protobuf `Content.dataMessage`
3738
SessionProtos::Content content = {};
39+
content.set_sigtimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(
40+
content_unix_ts.time_since_epoch())
41+
.count());
42+
3843
SessionProtos::DataMessage* data = content.mutable_datamessage();
3944
data->set_body(std::string(data_body));
4045

@@ -234,6 +239,8 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
234239
std::string plaintext;
235240
{
236241
SessionProtos::Content content = {};
242+
content.set_sigtimestamp(timestamp_ms.time_since_epoch().count());
243+
237244
SessionProtos::DataMessage* data = content.mutable_datamessage();
238245
data->set_body(std::string(data_body));
239246
plaintext = content.SerializeAsString();
@@ -268,7 +275,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
268275
&decrypt_keys,
269276
encrypt_result.ciphertext.data,
270277
encrypt_result.ciphertext.size,
271-
timestamp_ms.time_since_epoch().count(),
272278
pro_backend_ed_pk.data(),
273279
pro_backend_ed_pk.size(),
274280
error,
@@ -309,6 +315,7 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
309315
/*data_body*/ data_body,
310316
/*user_rotating_privkey*/ user_pro_ed_sk,
311317
/*pro_backend_privkey*/ pro_backend_ed_sk,
318+
/*content_unix_ts=*/timestamp_s,
312319
/*pro_expiry_unix_ts*/ timestamp_s,
313320
/*msg_bitset*/ {},
314321
/*profile_bitset*/ {});
@@ -388,7 +395,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
388395
&decrypt_keys,
389396
encrypt_result.ciphertext.data,
390397
encrypt_result.ciphertext.size,
391-
timestamp_ms.time_since_epoch().count(),
392398
pro_backend_ed_pk.data(),
393399
pro_backend_ed_pk.size(),
394400
error,
@@ -434,6 +440,7 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
434440
/*data_body*/ large_message,
435441
/*user_rotating_privkey*/ user_pro_ed_sk,
436442
/*pro_backend_privkey*/ pro_backend_ed_sk,
443+
/*content_unix_ts*/ timestamp_s,
437444
/*pro_expiry_unix_ts*/ timestamp_s,
438445
/*msg_bitset*/ pro_msg.bitset,
439446
/*proilfe_bitset*/ profile_bitset);
@@ -462,7 +469,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
462469
&decrypt_keys,
463470
encrypt_result.ciphertext.data,
464471
encrypt_result.ciphertext.size,
465-
timestamp_ms.time_since_epoch().count(),
466472
pro_backend_ed_pk.data(),
467473
pro_backend_ed_pk.size(),
468474
error,
@@ -565,7 +571,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
565571
&decrypt_keys,
566572
encrypt_result.ciphertext.data,
567573
encrypt_result.ciphertext.size,
568-
timestamp_ms.time_since_epoch().count(),
569574
pro_backend_ed_pk.data(),
570575
pro_backend_ed_pk.size(),
571576
error,
@@ -609,7 +614,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
609614
&decrypt_keys,
610615
encrypt_result.ciphertext.data,
611616
encrypt_result.ciphertext.size,
612-
timestamp_ms.time_since_epoch().count(),
613617
pro_backend_ed_pk.data(),
614618
pro_backend_ed_pk.size(),
615619
error,
@@ -639,11 +643,44 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
639643

640644
// Try decrypt with a timestamp past the pro proof expiry date
641645
{
646+
// Build protobuf `Content` message, serialise to `plaintext` and get it signed by the
647+
// user's "Session Pro" key into `sig_over_plaintext_with_user_pro_key`
648+
std::chrono::milliseconds bad_timestamp_ms =
649+
std::chrono::duration_cast<std::chrono::milliseconds>(
650+
protobuf_content.proof.expiry_unix_ts.time_since_epoch()) +
651+
std::chrono::seconds(1);
652+
653+
SerialisedProtobufContentWithProForTesting bad_protobuf_content =
654+
build_protobuf_content_with_session_pro(
655+
/*data_body*/ data_body,
656+
/*user_rotating_privkey*/ user_pro_ed_sk,
657+
/*pro_backend_privkey*/ pro_backend_ed_sk,
658+
/*content_unix_ts=*/
659+
std::chrono::sys_seconds(
660+
std::chrono::duration_cast<std::chrono::seconds>(
661+
bad_timestamp_ms)),
662+
/*pro_expiry_unix_ts*/ timestamp_s,
663+
/*msg_bitset*/ {},
664+
/*profile_bitset*/ {});
665+
666+
session_protocol_encoded_for_destination encrypt_bad_result =
667+
session_protocol_encode_for_1o1(
668+
bad_protobuf_content.plaintext.data(),
669+
bad_protobuf_content.plaintext.size(),
670+
keys.ed_sk0.data(),
671+
keys.ed_sk0.size(),
672+
bad_timestamp_ms.count(),
673+
&base_dest.recipient_pubkey,
674+
user_pro_ed_sk.data(),
675+
user_pro_ed_sk.size(),
676+
error,
677+
sizeof(error));
678+
REQUIRE(encrypt_bad_result.error_len_incl_null_terminator == 0);
679+
642680
session_protocol_decoded_envelope decrypt_result = session_protocol_decode_envelope(
643681
&decrypt_keys,
644-
encrypt_result.ciphertext.data,
645-
encrypt_result.ciphertext.size,
646-
protobuf_content.proof.expiry_unix_ts.time_since_epoch().count() + 1,
682+
encrypt_bad_result.ciphertext.data,
683+
encrypt_bad_result.ciphertext.size,
647684
pro_backend_ed_pk.data(),
648685
pro_backend_ed_pk.size(),
649686
error,
@@ -662,7 +699,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
662699
&decrypt_keys,
663700
encrypt_result.ciphertext.data,
664701
encrypt_result.ciphertext.size,
665-
protobuf_content.proof.expiry_unix_ts.time_since_epoch().count(),
666702
bad_pro_backend_ed_pk.data(),
667703
bad_pro_backend_ed_pk.size(),
668704
error,
@@ -684,7 +720,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
684720
&bad_decrypt_keys,
685721
encrypt_result.ciphertext.data,
686722
encrypt_result.ciphertext.size,
687-
protobuf_content.proof.expiry_unix_ts.time_since_epoch().count(),
688723
pro_backend_ed_pk.data(),
689724
pro_backend_ed_pk.size(),
690725
error,
@@ -707,9 +742,6 @@ TEST_CASE("Session protocol helpers C API", "[session-protocol][helpers]") {
707742
&multi_decrypt_keys,
708743
encrypt_result.ciphertext.data,
709744
encrypt_result.ciphertext.size,
710-
std::chrono::duration_cast<std::chrono::seconds>(
711-
protobuf_content.proof.expiry_unix_ts.time_since_epoch())
712-
.count(),
713745
pro_backend_ed_pk.data(),
714746
pro_backend_ed_pk.size(),
715747
error,

0 commit comments

Comments
 (0)