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