@@ -74,15 +74,13 @@ constexpr uint8_t voice_protocol_version = 8;
74
74
*/
75
75
static std::string external_ip;
76
76
77
- struct dave_transient_key {
77
+ struct dave_state {
78
78
std::shared_ptr<::mlspp::SignaturePrivateKey> mls_key;
79
79
std::vector<uint8_t > cached_commit;
80
80
uint64_t transition_id{0 };
81
- };
82
-
83
- struct dave_encryptors {
81
+ std::map<dpp::snowflake, std::unique_ptr<dave::Decryptor>> decryptors;
84
82
std::unique_ptr<dave::Encryptor> encryptor;
85
- std::unique_ptr<dave::Decryptor> decryptor ;
83
+ std::string privacy_code ;
86
84
};
87
85
88
86
/* *
@@ -187,6 +185,25 @@ size_t audio_mix(discord_voice_client& client, audio_mixer& mixer, opus_int32* p
187
185
max_samples = (std::max)(samples, max_samples);
188
186
return park_count + 1 ;
189
187
}
188
+
189
+ std::string generate_displayable_code (const std::vector<uint8_t >& data, size_t desired_length = 30 , size_t group_size = 5 ) {
190
+
191
+ const size_t group_modulus = std::pow (10 , group_size);
192
+ std::stringstream result;
193
+
194
+ for (size_t i = 0 ; i < desired_length; i += group_size) {
195
+ size_t group_value{0 };
196
+
197
+ for (size_t j = group_size; j > 0 ; --j) {
198
+ const size_t next_byte = data.at (i + (group_size - j));
199
+ group_value = (group_value << 8 ) | next_byte;
200
+ }
201
+ group_value %= group_modulus;
202
+ result << std::setw (group_size) << std::setfill (' 0' ) << std::to_string (group_value) << " " ;
203
+ }
204
+
205
+ return result.str ();
206
+ }
190
207
#endif
191
208
192
209
void discord_voice_client::voice_courier_loop (discord_voice_client& client, courier_shared_state_t & shared_state) {
@@ -512,8 +529,26 @@ std::vector<uint8_t> dave_binary_header_t::get_welcome_data(size_t length) const
512
529
return std::vector<uint8_t >(package + sizeof (uint16_t ), package + length - sizeof (dave_binary_header_t ));
513
530
}
514
531
515
- bool discord_voice_client::handle_frame (const std::string &data, ws_opcode opcode)
516
- {
532
+ std::string discord_voice_client::get_privacy_code () const {
533
+ return is_end_to_end_encrypted () ? mls_state->privacy_code : " " ;
534
+ }
535
+
536
+ void discord_voice_client::get_user_privacy_code (const dpp::snowflake user, privacy_code_callback_t callback) const {
537
+ if (!is_end_to_end_encrypted ()) {
538
+ callback (" " );
539
+ return ;
540
+ }
541
+ dave_session->GetPairwiseFingerprint (0x0000 , user.str (), [callback](const std::vector<uint8_t >& data) {
542
+ std::cout << dpp::utility::debug_dump ((uint8_t *)data.data (), data.size ());
543
+ callback (data.size () == 64 ? generate_displayable_code (data, 45 ) : " " );
544
+ });
545
+ }
546
+
547
+ bool discord_voice_client::is_end_to_end_encrypted () const {
548
+ return dave_session && mls_state && !mls_state->privacy_code .empty ();
549
+ }
550
+
551
+ bool discord_voice_client::handle_frame (const std::string &data, ws_opcode opcode) {
517
552
json j;
518
553
519
554
/* *
@@ -529,12 +564,8 @@ bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcod
529
564
530
565
dave_session->SetExternalSender (dave_header->get_data (data.length ()));
531
566
532
- encryptors = std::make_unique<dave_encryptors>();
533
- encryptors->encryptor = std::make_unique<dave::Encryptor>();
534
- /* *
535
- * TODO: There should be one of these per user but only one of the encryptor, above
536
- */
537
- encryptors->decryptor = std::make_unique<dave::Decryptor>();
567
+ mls_state->encryptor = std::make_unique<dave::Encryptor>();
568
+ mls_state->decryptors .clear ();
538
569
}
539
570
break ;
540
571
case voice_client_dave_mls_proposals: {
@@ -543,43 +574,54 @@ bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcod
543
574
std::optional<std::vector<uint8_t >> response = dave_session->ProcessProposals (dave_header->get_data (data.length ()), dave_mls_user_list);
544
575
if (response.has_value ()) {
545
576
auto r = response.value ();
546
- transient_key ->cached_commit = r;
577
+ mls_state ->cached_commit = r;
547
578
r.insert (r.begin (), voice_client_dave_mls_commit_message);
548
579
this ->write (std::string_view (reinterpret_cast <const char *>(r.data ()), r.size ()), OP_BINARY);
549
580
}
550
581
}
551
582
break ;
552
583
case voice_client_dave_announce_commit_transaction: {
553
584
log (ll_debug, " voice_client_dave_announce_commit_transaction" );
554
- auto r = dave_session->ProcessCommit (transient_key ->cached_commit );
585
+ auto r = dave_session->ProcessCommit (mls_state ->cached_commit );
555
586
for (const auto & user : dave_mls_user_list) {
556
587
log (ll_debug, " Setting decryptor key ratchet for user: " + user + " , protocol version: " + std::to_string (dave_session->GetProtocolVersion ()));
557
- encryptors->decryptor ->TransitionToKeyRatchet (dave_session->GetKeyRatchet (user));
588
+ dpp::snowflake u{user};
589
+ mls_state->decryptors .emplace (u, std::make_unique<dpp::dave::Decryptor>());
590
+ mls_state->decryptors .find (u)->second ->TransitionToKeyRatchet (dave_session->GetKeyRatchet (user));
558
591
}
559
- encryptors ->encryptor ->SetKeyRatchet (dave_session->GetKeyRatchet (creator->me .id .str ()));
592
+ mls_state ->encryptor ->SetKeyRatchet (dave_session->GetKeyRatchet (creator->me .id .str ()));
560
593
561
594
/* *
562
595
* https://www.ietf.org/archive/id/draft-ietf-mls-protocol-14.html#name-epoch-authenticators
563
596
* 9.7. Epoch Authenticators
564
597
* The main MLS key schedule provides a per-epoch epoch_authenticator. If one member of the group is being impersonated by an active attacker,
565
598
* the epoch_authenticator computed by their client will differ from those computed by the other group members.
566
599
*/
567
- auto epoch = dave_session->GetLastEpochAuthenticator ();
568
- log (ll_debug, " DAVE epoch authenticator: " + dpp::base64_encode ((unsigned char const *)epoch.data (), epoch.size ()));
600
+ mls_state->privacy_code = generate_displayable_code (dave_session->GetLastEpochAuthenticator ());
601
+ log (ll_debug, " E2EE Privacy Code: " + mls_state->privacy_code );
602
+ get_user_privacy_code (189759562910400512 , [this ](const std::string& code) {
603
+ log (ll_debug, " Test pairwise code: " + code);
604
+ });
569
605
}
570
606
break ;
571
607
case voice_client_dave_mls_welcome: {
572
- this ->transient_key ->transition_id = dave_header->get_welcome_transition_id ();
573
- log (ll_debug, " voice_client_dave_mls_welcome with transition id " + std::to_string (this ->transient_key ->transition_id ));
608
+ this ->mls_state ->transition_id = dave_header->get_welcome_transition_id ();
609
+ log (ll_debug, " voice_client_dave_mls_welcome with transition id " + std::to_string (this ->mls_state ->transition_id ));
574
610
auto r = dave_session->ProcessWelcome (dave_header->get_welcome_data (data.length ()), dave_mls_user_list);
575
611
if (r.has_value ()) {
576
- for (const auto & user_key_pair : r.value ()) {
577
- std::cout << " WEL: " << user_key_pair.first << " \n " ;
612
+ for (const auto & user : dave_mls_user_list) {
613
+ log (ll_debug, " Setting decryptor key ratchet for user: " + user + " , protocol version: " + std::to_string (dave_session->GetProtocolVersion ()));
614
+ dpp::snowflake u{user};
615
+ mls_state->decryptors .emplace (u, std::make_unique<dpp::dave::Decryptor>());
616
+ mls_state->decryptors .find (u)->second ->TransitionToKeyRatchet (dave_session->GetKeyRatchet (user));
578
617
}
579
- encryptors->encryptor ->SetKeyRatchet (dave_session->GetKeyRatchet (creator->me .id .str ()));
580
- } else {
581
- std::cout << " Welcome has no value\n " ;
618
+ mls_state->encryptor ->SetKeyRatchet (dave_session->GetKeyRatchet (creator->me .id .str ()));
582
619
}
620
+ mls_state->privacy_code = generate_displayable_code (dave_session->GetLastEpochAuthenticator ());
621
+ log (ll_debug, " E2EE Privacy Code: " + mls_state->privacy_code );
622
+ get_user_privacy_code (189759562910400512 , [this ](const std::string& code) {
623
+ log (ll_debug, " Test pairwise code: " + code);
624
+ });
583
625
}
584
626
break ;
585
627
default :
@@ -637,19 +679,19 @@ bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcod
637
679
}
638
680
break ;
639
681
case voice_client_dave_mls_invalid_commit_welcome: {
640
- this ->transient_key ->transition_id = j[" d" ][" transition_id" ];
641
- log (ll_debug, " voice_client_dave_mls_invalid_commit_welcome transition id " + std::to_string (this ->transient_key ->transition_id ));
682
+ this ->mls_state ->transition_id = j[" d" ][" transition_id" ];
683
+ log (ll_debug, " voice_client_dave_mls_invalid_commit_welcome transition id " + std::to_string (this ->mls_state ->transition_id ));
642
684
}
643
685
break ;
644
686
case voice_client_dave_execute_transition: {
645
687
log (ll_debug, " voice_client_dave_execute_transition" );
646
- this ->transient_key ->transition_id = j[" d" ][" transition_id" ];
688
+ this ->mls_state ->transition_id = j[" d" ][" transition_id" ];
647
689
json obj = {
648
690
{ " op" , voice_client_dave_transition_ready },
649
691
{
650
692
" d" ,
651
693
{
652
- { " transition_id" , this ->transient_key ->transition_id },
694
+ { " transition_id" , this ->mls_state ->transition_id },
653
695
}
654
696
}
655
697
};
@@ -669,7 +711,7 @@ bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcod
669
711
log (ll_debug, " voice_client_dave_prepare_epoch version=" + std::to_string (protocol_version) + " for epoch " + std::to_string (epoch));
670
712
if (epoch == 1 ) {
671
713
dave_session->Reset ();
672
- dave_session->Init (dave::MaxSupportedProtocolVersion (), channel_id, creator->me .id .str (), transient_key ->mls_key );
714
+ dave_session->Init (dave::MaxSupportedProtocolVersion (), channel_id, creator->me .id .str (), mls_state ->mls_key );
673
715
}
674
716
}
675
717
break ;
@@ -785,8 +827,8 @@ bool discord_voice_client::handle_frame(const std::string &data, ws_opcode opcod
785
827
nullptr , " " /* sessionid */ , [this ](std::string const & s1, std::string const & s2) {
786
828
log (ll_debug, " Dave session constructor callback: " + s1 + " , " + s2);
787
829
});
788
- transient_key = std::make_unique<dave_transient_key >();
789
- dave_session->Init (dave::MaxSupportedProtocolVersion (), channel_id, creator->me .id .str (), transient_key ->mls_key );
830
+ mls_state = std::make_unique<dave_state >();
831
+ dave_session->Init (dave::MaxSupportedProtocolVersion (), channel_id, creator->me .id .str (), mls_state ->mls_key );
790
832
auto key_response = dave_session->GetMarshalledKeyPackage ();
791
833
key_response.insert (key_response.begin (), voice_client_dave_mls_key_package);
792
834
this ->write (std::string_view (reinterpret_cast <const char *>(key_response.data ()), key_response.size ()), OP_BINARY);
0 commit comments