1use base64::prelude::*;
2use libsignal_core::{DeviceId, E164};
3use rand::{CryptoRng, Rng};
4use reqwest::Method;
5use std::collections::HashMap;
6use std::convert::{TryFrom, TryInto};
7
8use aes::cipher::{KeyIvInit, StreamCipher as _};
9use hmac::{digest::Output, KeyInit};
10use hmac::{Hmac, Mac};
11use libsignal_protocol::{
12 kem, Aci, GenericSignedPreKey, IdentityKey, IdentityKeyPair,
13 IdentityKeyStore, KeyPair, KyberPreKeyRecord, PrivateKey, ProtocolStore,
14 PublicKey, SenderKeyStore, ServiceIdKind, SignedPreKeyRecord, Timestamp,
15};
16use prost::Message;
17use serde::{Deserialize, Serialize};
18use sha2::{Digest, Sha256};
19use tracing_futures::Instrument;
20use zkgroup::profiles::ProfileKey;
21
22use crate::content::ContentBody;
23use crate::pre_keys::{
24 KyberPreKeyEntity, PreKeyEntity, PreKeysStore, SignedPreKeyEntity,
25 PRE_KEY_BATCH_SIZE, PRE_KEY_MINIMUM,
26};
27use crate::prelude::{MessageSender, MessageSenderError};
28use crate::proto::sync_message::PniChangeNumber;
29use crate::proto::{DeviceName, SyncMessage};
30use crate::provisioning::{generate_registration_id, ProvisioningSecrets};
31use crate::push_service::response::{device_limit_reached, error_mapper};
32use crate::push_service::{
33 AvatarWrite, HttpAuthOverride, SignalServiceResponse, DEFAULT_DEVICE_ID,
34};
35use crate::sender::OutgoingPushMessage;
36use crate::service_address::ServiceIdExt;
37use crate::session_store::SessionStoreExt;
38use crate::timestamp::TimestampExt as _;
39use crate::utils::{random_length_padding, BASE64_RELAXED};
40use crate::websocket::account::DeviceInfo;
41use crate::websocket::keys::PreKeyStatus;
42use crate::websocket::registration::CaptchaAttributes;
43use crate::websocket::{self, SignalWebSocket};
44use crate::{
45 configuration::Endpoint,
46 pre_keys::PreKeyState,
47 profile_cipher::{ProfileCipher, ProfileCipherError},
48 profile_name::ProfileName,
49 proto::{ProvisionEnvelope, ProvisionMessage, ProvisioningVersion},
50 provisioning::{ProvisioningCipher, ProvisioningError},
51 push_service::{PushService, ServiceError},
52 utils::serde_base64,
53 websocket::account::AccountAttributes,
54};
55
56error_mapper! {
59 get_provisioning_code_errors:
60 LENGTH_REQUIRED => fn device_limit_reached,
62}
63
64error_mapper! {
67 submit_challenge_errors:
68 PRECONDITION_REQUIRED => ChallengeNotAccepted,
70}
71
72type Aes256Ctr128BE = ctr::Ctr128BE<aes::Aes256>;
73
74pub struct AccountManager {
75 service: PushService,
76 websocket: SignalWebSocket<websocket::Identified>,
77 profile_key: Option<ProfileKey>,
78}
79
80#[derive(thiserror::Error, Debug)]
81pub enum ProfileManagerError {
82 #[error(transparent)]
83 ServiceError(#[from] ServiceError),
84 #[error(transparent)]
85 ProfileCipherError(#[from] ProfileCipherError),
86}
87
88#[derive(Debug, Default, Serialize, Deserialize, Clone)]
89pub struct Profile {
90 pub name: Option<ProfileName<String>>,
91 pub about: Option<String>,
92 pub about_emoji: Option<String>,
93 pub avatar: Option<String>,
94 pub unrestricted_unidentified_access: bool,
95}
96
97impl AccountManager {
98 pub fn new(
99 service: PushService,
100 websocket: SignalWebSocket<websocket::Identified>,
101 profile_key: Option<ProfileKey>,
102 ) -> Self {
103 Self {
104 service,
105 websocket,
106 profile_key,
107 }
108 }
109
110 #[allow(clippy::too_many_arguments)]
111 #[tracing::instrument(skip(self, protocol_store))]
112 pub async fn check_pre_keys<P: PreKeysStore>(
113 &mut self,
114 protocol_store: &mut P,
115 service_id_kind: ServiceIdKind,
116 ) -> Result<bool, ServiceError> {
117 let Some(signed_prekey_id) = protocol_store.signed_prekey_id().await?
118 else {
119 tracing::warn!("No signed prekey found");
120 return Ok(false);
121 };
122 let Some(kyber_prekey_id) =
125 protocol_store.last_resort_kyber_prekey_id().await?
126 else {
127 tracing::warn!("No last resort kyber prekey found");
128 return Ok(false);
129 };
130
131 let signed_prekey =
132 protocol_store.get_signed_pre_key(signed_prekey_id).await?;
133 let kyber_prekey =
134 protocol_store.get_kyber_pre_key(kyber_prekey_id).await?;
135
136 let mut hash = Sha256::default();
138 hash.update(
139 protocol_store
140 .get_identity_key_pair()
141 .await?
142 .public_key()
143 .serialize(),
144 );
145 hash.update((u32::from(signed_prekey_id) as u64).to_be_bytes());
146 hash.update(signed_prekey.public_key()?.serialize());
147 hash.update((u32::from(kyber_prekey_id) as u64).to_be_bytes());
148 hash.update(kyber_prekey.public_key()?.serialize());
149
150 self.websocket
151 .check_pre_keys(service_id_kind, hash.finalize().as_ref())
152 .await
153 }
154
155 #[allow(clippy::too_many_arguments)]
162 #[tracing::instrument(skip(self, protocol_store))]
163 pub async fn update_pre_key_bundle<P: PreKeysStore>(
164 &mut self,
165 protocol_store: &mut P,
166 service_id_kind: ServiceIdKind,
167 use_last_resort_key: bool,
168 ) -> Result<(), ServiceError> {
169 let prekey_status = match self
170 .websocket
171 .get_pre_key_status(service_id_kind)
172 .instrument(tracing::span!(
173 tracing::Level::DEBUG,
174 "Fetching pre key status"
175 ))
176 .await
177 {
178 Ok(status) => status,
179 Err(ServiceError::Unauthorized) => {
180 tracing::info!("Got Unauthorized when fetching pre-key status. Assuming first installment.");
181 PreKeyStatus {
184 count: 0,
185 pq_count: 0,
186 }
187 },
188 Err(e) => return Err(e),
189 };
190 tracing::trace!("Remaining pre-keys on server: {:?}", prekey_status);
191
192 let check_pre_keys = self
193 .check_pre_keys(protocol_store, service_id_kind)
194 .instrument(tracing::span!(
195 tracing::Level::DEBUG,
196 "Checking pre keys"
197 ))
198 .await?;
199 if !check_pre_keys {
200 tracing::info!(
201 "Last resort pre-keys are not up to date; refreshing."
202 );
203 } else {
204 tracing::debug!("Last resort pre-keys are up to date.");
205 }
206
207 if check_pre_keys
212 && (prekey_status.count >= PRE_KEY_MINIMUM
213 && prekey_status.pq_count >= PRE_KEY_MINIMUM)
214 {
215 if protocol_store.signed_pre_keys_count().await? > 0
216 && protocol_store.kyber_pre_keys_count(true).await? > 0
217 && protocol_store.signed_prekey_id().await?.is_some()
218 && protocol_store
219 .last_resort_kyber_prekey_id()
220 .await?
221 .is_some()
222 {
223 tracing::debug!("Available keys sufficient");
224 return Ok(());
225 }
226 tracing::info!("Available keys sufficient; forcing refresh.");
227 }
228
229 let identity_key_pair = protocol_store
230 .get_identity_key_pair()
231 .instrument(tracing::trace_span!("get identity key pair"))
232 .await?;
233
234 let last_resort_keys = protocol_store
235 .load_last_resort_kyber_pre_keys()
236 .instrument(tracing::trace_span!("fetch last resort key"))
237 .await?;
238
239 let has_last_resort_key = !last_resort_keys.is_empty();
241
242 let (pre_keys, signed_pre_key, pq_pre_keys, pq_last_resort_key) =
243 crate::pre_keys::replenish_pre_keys(
244 protocol_store,
245 &mut rand::rng(),
246 &identity_key_pair,
247 use_last_resort_key && !has_last_resort_key,
248 PRE_KEY_BATCH_SIZE,
249 PRE_KEY_BATCH_SIZE,
250 )
251 .await?;
252
253 let pq_last_resort_key = if has_last_resort_key {
254 if last_resort_keys.len() > 1 {
255 tracing::warn!(
256 "More than one last resort key found; only uploading first"
257 );
258 }
259 Some(KyberPreKeyEntity::try_from(last_resort_keys[0].clone())?)
260 } else {
261 pq_last_resort_key
262 .map(KyberPreKeyEntity::try_from)
263 .transpose()?
264 };
265
266 let identity_key = *identity_key_pair.identity_key();
267
268 let pre_keys: Vec<_> = pre_keys
269 .into_iter()
270 .map(PreKeyEntity::try_from)
271 .collect::<Result<_, _>>()?;
272 let signed_pre_key = signed_pre_key.try_into()?;
273 let pq_pre_keys: Vec<_> = pq_pre_keys
274 .into_iter()
275 .map(KyberPreKeyEntity::try_from)
276 .collect::<Result<_, _>>()?;
277
278 tracing::info!(
279 "Uploading pre-keys: {} one-time, {} PQ, {} PQ last resort",
280 pre_keys.len(),
281 pq_pre_keys.len(),
282 if pq_last_resort_key.is_some() { 1 } else { 0 }
283 );
284
285 let pre_key_state = PreKeyState {
286 pre_keys,
287 signed_pre_key,
288 identity_key,
289 pq_pre_keys,
290 pq_last_resort_key,
291 };
292
293 self.websocket
294 .register_pre_keys(service_id_kind, pre_key_state)
295 .instrument(tracing::span!(
296 tracing::Level::DEBUG,
297 "Uploading pre keys"
298 ))
299 .await?;
300
301 Ok(())
302 }
303
304 async fn new_device_provisioning_code(
305 &mut self,
306 ) -> Result<String, ServiceError> {
307 #[derive(serde::Deserialize)]
308 #[serde(rename_all = "camelCase")]
309 struct DeviceCode {
310 verification_code: String,
311 }
312
313 let dc: DeviceCode = self
314 .service
315 .request(
316 Method::GET,
317 Endpoint::service("/v1/devices/provisioning/code"),
318 HttpAuthOverride::NoOverride,
319 )?
320 .send()
321 .await?
322 .service_error_for_status_with(get_provisioning_code_errors)
323 .await?
324 .json()
325 .await?;
326
327 Ok(dc.verification_code)
328 }
329
330 async fn send_provisioning_message(
331 &mut self,
332 destination: &str,
333 env: ProvisionEnvelope,
334 ) -> Result<(), ServiceError> {
335 #[derive(serde::Serialize)]
336 struct ProvisioningMessage {
337 body: String,
338 }
339
340 let body = env.encode_to_vec();
341
342 self.service
343 .request(
344 Method::PUT,
345 Endpoint::service(format!("/v1/provisioning/{destination}")),
346 HttpAuthOverride::NoOverride,
347 )?
348 .json(&ProvisioningMessage {
349 body: BASE64_RELAXED.encode(body),
350 })
351 .send()
352 .await?
353 .service_error_for_status()
354 .await?;
355
356 Ok(())
357 }
358
359 pub async fn link_device<R: Rng + CryptoRng>(
373 &mut self,
374 csprng: &mut R,
375 url: url::Url,
376 aci_identity_store: &dyn IdentityKeyStore,
377 pni_identity_store: &dyn IdentityKeyStore,
378 secrets: ProvisioningSecrets,
379 ) -> Result<(), ProvisioningError> {
380 let ProvisioningSecrets {
381 credentials,
382 ephemeral_backup_key,
383 account_entropy_pool,
384 media_root_backup_key,
385 } = secrets;
386
387 let query: HashMap<_, _> = url.query_pairs().collect();
388 let ephemeral_id =
389 query.get("uuid").ok_or(ProvisioningError::MissingUuid)?;
390 let pub_key = query
391 .get("pub_key")
392 .ok_or(ProvisioningError::MissingPublicKey)?;
393
394 let pub_key = BASE64_RELAXED
395 .decode(&**pub_key)
396 .map_err(|e| ProvisioningError::InvalidPublicKey(e.into()))?;
397 let pub_key = PublicKey::deserialize(&pub_key)
398 .map_err(|e| ProvisioningError::InvalidPublicKey(e.into()))?;
399
400 let aci_identity_key_pair =
401 aci_identity_store.get_identity_key_pair().await?;
402 let pni_identity_key_pair =
403 pni_identity_store.get_identity_key_pair().await?;
404
405 if credentials.aci.is_none() {
406 tracing::warn!("No local ACI set");
407 }
408 if credentials.pni.is_none() {
409 tracing::warn!("No local PNI set");
410 }
411
412 let provisioning_code = self.new_device_provisioning_code().await?;
413
414 let msg = ProvisionMessage {
416 aci_identity_key_public: Some(
417 aci_identity_key_pair.public_key().serialize().into_vec(),
418 ),
419 aci_identity_key_private: Some(
420 aci_identity_key_pair.private_key().serialize(),
421 ),
422 pni_identity_key_public: Some(
423 pni_identity_key_pair.public_key().serialize().into_vec(),
424 ),
425 pni_identity_key_private: Some(
426 pni_identity_key_pair.private_key().serialize(),
427 ),
428 aci: credentials.aci.as_ref().map(|u| u.to_string()),
429 pni: credentials.pni.as_ref().map(uuid::Uuid::to_string),
430 number: Some(credentials.e164()),
431 provisioning_code: Some(provisioning_code),
432 user_agent: None,
433 profile_key: self.profile_key.as_ref().map(|x| x.bytes.to_vec()),
434 read_receipts: None,
436 provisioning_version: Some(i32::from(
437 ProvisioningVersion::TabletSupport,
438 ) as _),
439 ephemeral_backup_key: ephemeral_backup_key.map(|k| k.to_vec()), account_entropy_pool: Some(account_entropy_pool.to_string()),
441 media_root_backup_key: media_root_backup_key.map(|k| k.to_vec()), aci_binary: credentials.aci.map(|u| u.into_bytes().into()),
443 pni_binary: credentials.pni.map(|u| u.into_bytes().into()),
444 };
445
446 let cipher = ProvisioningCipher::from_public(pub_key);
447
448 let encrypted = cipher.encrypt(csprng, msg)?;
449 self.send_provisioning_message(ephemeral_id, encrypted)
450 .await?;
451 Ok(())
452 }
453
454 pub async fn linked_devices(
455 &mut self,
456 aci_identity_store: &dyn IdentityKeyStore,
457 ) -> Result<Vec<DeviceInfo>, ServiceError> {
458 let device_infos = self.websocket.devices().await?;
459 let aci_identity_keypair =
460 aci_identity_store.get_identity_key_pair().await?;
461
462 device_infos
463 .into_iter()
464 .map(|i| {
465 Ok(DeviceInfo {
466 id: i.id,
467 name: i.name.and_then(|s| {
468 match decrypt_device_name_from_device_info(
469 &s,
470 &aci_identity_keypair,
471 ) {
472 Ok(name) => Some(name),
473 Err(e) => {
474 tracing::error!("{e}");
475 None
476 },
477 }
478 }),
479 registration_id: i.registration_id,
480 last_seen: i.last_seen,
481 created_at: decrypt_device_created_at_from_device_info(
482 i.id,
483 i.registration_id,
484 &i.created_at_ciphertext,
485 &aci_identity_keypair,
486 )?,
487 })
488 })
489 .collect()
490 }
491
492 pub async fn upload_versioned_profile_without_avatar<
503 R: Rng + CryptoRng,
504 S: AsRef<str>,
505 >(
506 &mut self,
507 aci: libsignal_protocol::Aci,
508 name: ProfileName<S>,
509 about: Option<String>,
510 about_emoji: Option<String>,
511 retain_avatar: bool,
512 csprng: &mut R,
513 ) -> Result<(), ProfileManagerError> {
514 self.upload_versioned_profile::<std::io::Cursor<Vec<u8>>, _, _>(
515 aci,
516 name,
517 about,
518 about_emoji,
519 if retain_avatar {
520 AvatarWrite::RetainAvatar
521 } else {
522 AvatarWrite::NoAvatar
523 },
524 csprng,
525 )
526 .await?;
527 Ok(())
528 }
529
530 pub async fn retrieve_profile(
531 &mut self,
532 address: Aci,
533 ) -> Result<Profile, ProfileManagerError> {
534 let profile_key =
535 self.profile_key.expect("set profile key in AccountManager");
536
537 let encrypted_profile = self
538 .websocket
539 .retrieve_profile_by_id(address, Some(profile_key))
540 .await?;
541
542 let profile_cipher = ProfileCipher::new(profile_key);
543 Ok(profile_cipher.decrypt(encrypted_profile)?)
544 }
545
546 pub async fn upload_versioned_profile<
552 's,
553 C: std::io::Read + Send + 's,
554 R: Rng + CryptoRng,
555 S: AsRef<str>,
556 >(
557 &mut self,
558 aci: libsignal_protocol::Aci,
559 name: ProfileName<S>,
560 about: Option<String>,
561 about_emoji: Option<String>,
562 avatar: AvatarWrite<&'s mut C>,
563 csprng: &mut R,
564 ) -> Result<Option<String>, ProfileManagerError> {
565 let profile_key =
566 self.profile_key.expect("set profile key in AccountManager");
567 let profile_cipher = ProfileCipher::new(profile_key);
568
569 let name = profile_cipher.encrypt_name(name.as_ref(), csprng)?;
571 let about = about.unwrap_or_default();
572 let about = profile_cipher.encrypt_about(about, csprng)?;
573 let about_emoji = about_emoji.unwrap_or_default();
574 let about_emoji = profile_cipher.encrypt_emoji(about_emoji, csprng)?;
575
576 if matches!(avatar, AvatarWrite::NewAvatar(_)) {
578 unimplemented!("Setting avatar requires ProfileCipherStream")
581 }
582
583 let profile_key = profile_cipher.into_inner();
584 let commitment = profile_key.get_commitment(aci);
585 let profile_key_version = profile_key.get_profile_key_version(aci);
586
587 Ok(self
588 .websocket
589 .write_profile::<C, S>(
590 &profile_key_version,
591 &name,
592 &about,
593 &about_emoji,
594 &commitment,
595 avatar,
596 )
597 .await?)
598 }
599
600 pub async fn set_account_attributes(
604 &mut self,
605 attributes: AccountAttributes,
606 ) -> Result<(), ServiceError> {
607 self.websocket.set_account_attributes(attributes).await
608 }
609
610 pub async fn update_device_name<R: Rng + CryptoRng>(
612 &mut self,
613 device_id: libsignal_core::DeviceId,
614 device_name: &str,
615 aci: Aci,
616 aci_identity_store: &dyn IdentityKeyStore,
617 csprng: &mut R,
618 ) -> Result<(), ServiceError> {
619 let addr = aci.to_protocol_address(device_id).unwrap();
620 let public_key = aci_identity_store.get_identity(&addr).await?;
621 let Some(public_key) = public_key else {
622 return Err(ServiceError::SendError {
623 reason: format!("public key for device {addr:?} not found"),
624 });
625 };
626 let encrypted_device_name =
627 encrypt_device_name(csprng, device_name, &public_key)?;
628
629 #[derive(Serialize)]
630 #[serde(rename_all = "camelCase")]
631 struct Data {
632 #[serde(with = "serde_base64")]
633 device_name: Vec<u8>,
634 }
635
636 self.service
637 .request(
638 Method::PUT,
639 Endpoint::service(format!(
640 "/v1/accounts/name?deviceId={}",
641 device_id
642 )),
643 HttpAuthOverride::NoOverride,
644 )?
645 .json(&Data {
646 device_name: encrypted_device_name.encode_to_vec(),
647 })
648 .send()
649 .await?
650 .service_error_for_status()
651 .await?;
652
653 Ok(())
654 }
655
656 pub async fn submit_recaptcha_challenge(
663 &mut self,
664 token: &str,
665 captcha: &str,
666 ) -> Result<(), ServiceError> {
667 self.service
668 .request(
669 Method::PUT,
670 Endpoint::service("/v1/challenge"),
671 HttpAuthOverride::NoOverride,
672 )?
673 .json(&CaptchaAttributes {
674 challenge_type: "captcha",
675 token,
676 captcha,
677 })
678 .send()
679 .await?
680 .service_error_for_status_with(submit_challenge_errors)
681 .await?;
682
683 Ok(())
684 }
685
686 #[tracing::instrument(skip(self, aci_protocol_store, pni_protocol_store, sender, local_aci, csprng), fields(local_aci = local_aci.service_id_string()))]
692 pub async fn pnp_initialize_devices<
693 R: Rng + CryptoRng,
694 AciStore: PreKeysStore + SessionStoreExt,
695 PniStore: PreKeysStore,
696 AciOrPni: ProtocolStore + SenderKeyStore + SessionStoreExt + Sync + Clone,
697 >(
698 &mut self,
699 aci_protocol_store: &mut AciStore,
700 pni_protocol_store: &mut PniStore,
701 mut sender: MessageSender<AciOrPni>,
702 local_aci: Aci,
703 e164: E164,
704 csprng: &mut R,
705 ) -> Result<(), MessageSenderError> {
706 let pni_identity_key_pair =
707 pni_protocol_store.get_identity_key_pair().await?;
708
709 let pni_identity_key = pni_identity_key_pair.identity_key();
710
711 let local_device_ids = aci_protocol_store
713 .get_sub_device_sessions(&local_aci.into())
714 .await?;
715
716 let mut device_messages =
717 Vec::<OutgoingPushMessage>::with_capacity(local_device_ids.len());
718 let mut device_pni_signed_prekeys =
719 HashMap::<String, SignedPreKeyEntity>::with_capacity(
720 local_device_ids.len(),
721 );
722 let mut device_pni_last_resort_kyber_prekeys =
723 HashMap::<String, KyberPreKeyEntity>::with_capacity(
724 local_device_ids.len(),
725 );
726 let mut pni_registration_ids =
727 HashMap::<String, u32>::with_capacity(local_device_ids.len());
728
729 let signature_valid_on_each_signed_pre_key = true;
730 for local_device_id in
731 std::iter::once(*DEFAULT_DEVICE_ID).chain(local_device_ids)
732 {
733 let local_protocol_address =
734 local_aci.to_protocol_address(local_device_id)?;
735 let span = tracing::trace_span!(
736 "filtering devices",
737 address = %local_protocol_address
738 );
739 if (local_device_id != *DEFAULT_DEVICE_ID)
741 && aci_protocol_store
742 .load_session(&local_protocol_address)
743 .instrument(span)
744 .await?
745 .is_none()
746 {
747 tracing::warn!(
748 "No session with device {}, skipping PNI provisioning",
749 local_device_id
750 );
751 continue;
752 }
753 let (
754 _pre_keys,
755 signed_pre_key,
756 _kyber_pre_keys,
757 last_resort_kyber_prekey,
758 ) = if local_device_id == *DEFAULT_DEVICE_ID {
759 crate::pre_keys::replenish_pre_keys(
760 pni_protocol_store,
761 csprng,
762 &pni_identity_key_pair,
763 true,
764 0,
765 0,
766 )
767 .await?
768 } else {
769 let signed_pre_key_pair = KeyPair::generate(csprng);
771 let signed_pre_key_public = signed_pre_key_pair.public_key;
772 let signed_pre_key_signature = pni_identity_key_pair
773 .private_key()
774 .calculate_signature(
775 &signed_pre_key_public.serialize(),
776 csprng,
777 )
778 .map_err(MessageSenderError::InvalidPrivateKey)?;
779
780 let signed_prekey_record = SignedPreKeyRecord::new(
781 csprng.random_range::<u32, _>(0..0xFFFFFF).into(),
782 Timestamp::now(),
783 &signed_pre_key_pair,
784 &signed_pre_key_signature,
785 );
786
787 let kyber_pre_key_record = KyberPreKeyRecord::generate(
789 kem::KeyType::Kyber1024,
790 csprng.random_range::<u32, _>(0..0xFFFFFF).into(),
791 pni_identity_key_pair.private_key(),
792 )?;
793 (
794 vec![],
795 signed_prekey_record,
796 vec![],
797 Some(kyber_pre_key_record),
798 )
799 };
800
801 let registration_id = if local_device_id == *DEFAULT_DEVICE_ID {
802 pni_protocol_store.get_local_registration_id().await?
803 } else {
804 loop {
805 let regid = generate_registration_id(csprng);
806 if !pni_registration_ids.iter().any(|(_k, v)| *v == regid) {
807 break regid;
808 }
809 }
810 };
811
812 let local_device_id_s = local_device_id.to_string();
813 device_pni_signed_prekeys.insert(
814 local_device_id_s.clone(),
815 SignedPreKeyEntity::try_from(&signed_pre_key)?,
816 );
817 device_pni_last_resort_kyber_prekeys.insert(
818 local_device_id_s.clone(),
819 KyberPreKeyEntity::try_from(
820 last_resort_kyber_prekey
821 .as_ref()
822 .expect("requested last resort key"),
823 )?,
824 );
825 pni_registration_ids
826 .insert(local_device_id_s.clone(), registration_id);
827
828 assert!(_pre_keys.is_empty());
829 assert!(_kyber_pre_keys.is_empty());
830
831 if local_device_id == *DEFAULT_DEVICE_ID {
832 continue;
835 }
836 let msg = SyncMessage {
838 content: Some(
839 crate::proto::sync_message::Content::PniChangeNumber(
840 PniChangeNumber {
841 identity_key_pair: Some(
842 pni_identity_key_pair.serialize().to_vec(),
843 ),
844 signed_pre_key: Some(signed_pre_key.serialize()?),
845 last_resort_kyber_pre_key: Some(
846 last_resort_kyber_prekey
847 .expect("requested last resort key")
848 .serialize()?,
849 ),
850 registration_id: Some(registration_id),
851 new_e164: Some(e164.to_string()),
852 },
853 ),
854 ),
855 padding: Some(random_length_padding(csprng, 512)),
856 ..SyncMessage::default()
857 };
858 let content: ContentBody = msg.into();
859 let msg = sender
860 .create_encrypted_message(
861 &local_aci.into(),
862 None,
863 local_device_id,
864 &content.into_proto().encode_to_vec(),
865 )
866 .await?;
867 device_messages.push(msg);
868 }
869
870 self.websocket
871 .distribute_pni_keys(
872 pni_identity_key,
873 device_messages,
874 device_pni_signed_prekeys,
875 device_pni_last_resort_kyber_prekeys,
876 pni_registration_ids,
877 signature_valid_on_each_signed_pre_key,
878 )
879 .await?;
880
881 Ok(())
882 }
883}
884
885fn calculate_hmac256(
886 mac_key: &[u8],
887 ciphertext: &[u8],
888) -> Result<Output<Hmac<Sha256>>, ServiceError> {
889 let mut mac = Hmac::<Sha256>::new_from_slice(mac_key)
890 .map_err(|_| ServiceError::MacError)?;
891 mac.update(ciphertext);
892 Ok(mac.finalize().into_bytes())
893}
894
895pub fn encrypt_device_name<R: rand::Rng + rand::CryptoRng>(
896 csprng: &mut R,
897 device_name: &str,
898 identity_public: &IdentityKey,
899) -> Result<DeviceName, ServiceError> {
900 let plaintext = device_name.as_bytes().to_vec();
901 let ephemeral_key_pair = KeyPair::generate(csprng);
902
903 let master_secret = ephemeral_key_pair
904 .private_key
905 .calculate_agreement(identity_public.public_key())?;
906
907 let key1 = calculate_hmac256(&master_secret, b"auth")?;
908 let synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
909 let synthetic_iv = &synthetic_iv[..16];
910
911 let key2 = calculate_hmac256(&master_secret, b"cipher")?;
912 let cipher_key = calculate_hmac256(&key2, synthetic_iv)?;
913
914 let mut ciphertext = plaintext;
915
916 const IV: [u8; 16] = [0; 16];
917 let mut cipher = Aes256Ctr128BE::new(&cipher_key, &IV.into());
918 cipher.apply_keystream(&mut ciphertext);
919
920 let device_name = DeviceName {
921 ephemeral_public: Some(
922 ephemeral_key_pair.public_key.serialize().to_vec(),
923 ),
924 synthetic_iv: Some(synthetic_iv.to_vec()),
925 ciphertext: Some(ciphertext),
926 };
927
928 Ok(device_name)
929}
930
931fn decrypt_device_name_from_device_info(
932 string: &str,
933 aci: &IdentityKeyPair,
934) -> Result<String, ServiceError> {
935 let Ok(data) = BASE64_RELAXED.decode(string) else {
936 tracing::trace!(
937 "Device name not base64 encoded, assuming plaintext: {}",
938 string.to_string()
939 );
940 return Ok(string.to_string());
941 };
942 let name = match DeviceName::decode(data.as_slice()) {
943 Ok(decoded) => decoded,
944 Err(_) => {
945 tracing::trace!(
946 "Decoding device name failed, assuming plaintext: {}",
947 string.to_string()
948 );
949 return Ok(string.to_string());
950 },
951 };
952 crate::decrypt_device_name(aci.private_key(), &name)
953}
954
955fn decrypt_device_created_at_from_device_info(
957 id: DeviceId,
958 registration_id: i32,
959 string: &str,
960 aci: &IdentityKeyPair,
961) -> Result<chrono::DateTime<chrono::Utc>, ServiceError> {
962 use signal_crypto::SimpleHpkeReceiver;
963
964 let mut associated_data = [0; 5];
965 associated_data[0] = id.into();
966 associated_data[1..].copy_from_slice(®istration_id.to_be_bytes());
967
968 let data = BASE64_RELAXED.decode(string)?;
969
970 let result =
971 aci.private_key()
972 .open(b"deviceCreatedAt", &associated_data, &data)?;
973
974 let timestamp = i64::from_be_bytes(result.try_into().map_err(|_| {
975 ServiceError::DecryptDeviceInfoFieldError("created-at")
976 })?);
977
978 chrono::DateTime::<chrono::Utc>::from_timestamp_millis(timestamp)
979 .ok_or(ServiceError::DecryptDeviceInfoFieldError("created-at"))
980}
981
982pub fn decrypt_device_name(
983 private_key: &PrivateKey,
984 device_name: &DeviceName,
985) -> Result<String, ServiceError> {
986 let DeviceName {
987 ephemeral_public: Some(ephemeral_public),
988 synthetic_iv: Some(synthetic_iv),
989 ciphertext: Some(ciphertext),
990 } = device_name
991 else {
992 return Err(ServiceError::DecryptDeviceInfoFieldError("name"));
993 };
994
995 let synthetic_iv: [u8; 16] = synthetic_iv[..synthetic_iv.len().min(16)]
996 .try_into()
997 .map_err(|_| ServiceError::MacError)?;
998
999 let ephemeral_public = PublicKey::deserialize(ephemeral_public)?;
1000
1001 let master_secret = private_key.calculate_agreement(&ephemeral_public)?;
1002 let key2 = calculate_hmac256(&master_secret, b"cipher")?;
1003 let cipher_key = calculate_hmac256(&key2, &synthetic_iv)?;
1004
1005 let mut plaintext = ciphertext.to_vec();
1006 const IV: [u8; 16] = [0; 16];
1007 let mut cipher = Aes256Ctr128BE::new(&cipher_key, &IV.into());
1008 cipher.apply_keystream(&mut plaintext);
1009
1010 let key1 = calculate_hmac256(&master_secret, b"auth")?;
1011 let our_synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
1012 let our_synthetic_iv = &our_synthetic_iv[..16];
1013
1014 if synthetic_iv != our_synthetic_iv {
1015 Err(ServiceError::MacError)
1016 } else {
1017 Ok(String::from_utf8_lossy(&plaintext).to_string())
1018 }
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023 use crate::utils::BASE64_RELAXED;
1024 use base64::Engine;
1025 use libsignal_protocol::{IdentityKeyPair, PrivateKey, PublicKey};
1026
1027 use super::DeviceName;
1028
1029 #[test]
1030 fn encrypt_device_name() -> anyhow::Result<()> {
1031 let input_device_name = "Nokia 3310 Millenial Edition";
1032 let mut csprng = rand::rng();
1033 let identity = IdentityKeyPair::generate(&mut csprng);
1034
1035 let device_name = super::encrypt_device_name(
1036 &mut csprng,
1037 input_device_name,
1038 identity.identity_key(),
1039 )?;
1040
1041 let decrypted_device_name =
1042 super::decrypt_device_name(identity.private_key(), &device_name)?;
1043
1044 assert_eq!(input_device_name, decrypted_device_name);
1045
1046 Ok(())
1047 }
1048
1049 #[test]
1050 fn decrypt_device_name() -> anyhow::Result<()> {
1051 let ephemeral_private_key = PrivateKey::deserialize(
1052 &BASE64_RELAXED
1053 .decode("0CgxHjwwblXjvX8sD5wZDWdYToMRf+CZSlgaUrxCGVo=")?,
1054 )?;
1055 let ephemeral_public_key = PublicKey::deserialize(
1056 &BASE64_RELAXED
1057 .decode("BcZS+Lt6yAKbEpXnRX+I5wHqesuvu93Q2V+fjidwW8R6")?,
1058 )?;
1059
1060 let device_name = DeviceName {
1061 ephemeral_public: Some(ephemeral_public_key.serialize().to_vec()),
1062 synthetic_iv: Some(
1063 BASE64_RELAXED.decode("86gekHGmltnnZ9QARhiFcg==")?,
1064 ),
1065 ciphertext: Some(
1066 BASE64_RELAXED
1067 .decode("MtJ9/9KBWLBVAxfZJD4pLKzP4q+iodRJeCc+/A==")?,
1068 ),
1069 };
1070
1071 let decrypted_device_name =
1072 super::decrypt_device_name(&ephemeral_private_key, &device_name)?;
1073
1074 assert_eq!(decrypted_device_name, "Nokia 3310 Millenial Edition");
1075
1076 Ok(())
1077 }
1078}