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;
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 master_key,
366 ephemeral_backup_key,
367 account_entropy_pool,
368 media_root_backup_key,
369 } = secrets;
370
371 let query: HashMap<_, _> = url.query_pairs().collect();
372 let ephemeral_id =
373 query.get("uuid").ok_or(ProvisioningError::MissingUuid)?;
374 let pub_key = query
375 .get("pub_key")
376 .ok_or(ProvisioningError::MissingPublicKey)?;
377
378 let pub_key = BASE64_RELAXED
379 .decode(&**pub_key)
380 .map_err(|e| ProvisioningError::InvalidPublicKey(e.into()))?;
381 let pub_key = PublicKey::deserialize(&pub_key)
382 .map_err(|e| ProvisioningError::InvalidPublicKey(e.into()))?;
383
384 let aci_identity_key_pair =
385 aci_identity_store.get_identity_key_pair().await?;
386 let pni_identity_key_pair =
387 pni_identity_store.get_identity_key_pair().await?;
388
389 if credentials.aci.is_none() {
390 tracing::warn!("No local ACI set");
391 }
392 if credentials.pni.is_none() {
393 tracing::warn!("No local PNI set");
394 }
395
396 let provisioning_code = self.new_device_provisioning_code().await?;
397
398 let msg = ProvisionMessage {
400 aci_identity_key_public: Some(
401 aci_identity_key_pair.public_key().serialize().into_vec(),
402 ),
403 aci_identity_key_private: Some(
404 aci_identity_key_pair.private_key().serialize(),
405 ),
406 pni_identity_key_public: Some(
407 pni_identity_key_pair.public_key().serialize().into_vec(),
408 ),
409 pni_identity_key_private: Some(
410 pni_identity_key_pair.private_key().serialize(),
411 ),
412 aci: credentials.aci.as_ref().map(|u| u.to_string()),
413 pni: credentials.pni.as_ref().map(uuid::Uuid::to_string),
414 number: Some(credentials.e164()),
415 provisioning_code: Some(provisioning_code),
416 user_agent: None,
417 profile_key: self.profile_key.as_ref().map(|x| x.bytes.to_vec()),
418 read_receipts: None,
420 provisioning_version: Some(i32::from(
421 ProvisioningVersion::TabletSupport,
422 ) as _),
423 master_key: master_key.map(|x| x.into()),
424 ephemeral_backup_key: ephemeral_backup_key.map(|k| k.to_vec()), account_entropy_pool: Some(account_entropy_pool.to_string()),
426 media_root_backup_key: media_root_backup_key.map(|k| k.to_vec()), aci_binary: credentials.aci.map(|u| u.into_bytes().into()),
428 pni_binary: credentials.pni.map(|u| u.into_bytes().into()),
429 };
430
431 let cipher = ProvisioningCipher::from_public(pub_key);
432
433 let encrypted = cipher.encrypt(csprng, msg)?;
434 self.send_provisioning_message(ephemeral_id, encrypted)
435 .await?;
436 Ok(())
437 }
438
439 pub async fn linked_devices(
440 &mut self,
441 aci_identity_store: &dyn IdentityKeyStore,
442 ) -> Result<Vec<DeviceInfo>, ServiceError> {
443 let device_infos = self.websocket.devices().await?;
444 let aci_identity_keypair =
445 aci_identity_store.get_identity_key_pair().await?;
446
447 device_infos
448 .into_iter()
449 .map(|i| {
450 Ok(DeviceInfo {
451 id: i.id,
452 name: i.name.and_then(|s| {
453 match decrypt_device_name_from_device_info(
454 &s,
455 &aci_identity_keypair,
456 ) {
457 Ok(name) => Some(name),
458 Err(e) => {
459 tracing::error!("{e}");
460 None
461 },
462 }
463 }),
464 registration_id: i.registration_id,
465 last_seen: i.last_seen,
466 created_at: decrypt_device_created_at_from_device_info(
467 i.id,
468 i.registration_id,
469 &i.created_at_ciphertext,
470 &aci_identity_keypair,
471 )?,
472 })
473 })
474 .collect()
475 }
476
477 pub async fn upload_versioned_profile_without_avatar<
488 R: Rng + CryptoRng,
489 S: AsRef<str>,
490 >(
491 &mut self,
492 aci: libsignal_protocol::Aci,
493 name: ProfileName<S>,
494 about: Option<String>,
495 about_emoji: Option<String>,
496 retain_avatar: bool,
497 csprng: &mut R,
498 ) -> Result<(), ProfileManagerError> {
499 self.upload_versioned_profile::<std::io::Cursor<Vec<u8>>, _, _>(
500 aci,
501 name,
502 about,
503 about_emoji,
504 if retain_avatar {
505 AvatarWrite::RetainAvatar
506 } else {
507 AvatarWrite::NoAvatar
508 },
509 csprng,
510 )
511 .await?;
512 Ok(())
513 }
514
515 pub async fn retrieve_profile(
516 &mut self,
517 address: Aci,
518 ) -> Result<Profile, ProfileManagerError> {
519 let profile_key =
520 self.profile_key.expect("set profile key in AccountManager");
521
522 let encrypted_profile = self
523 .websocket
524 .retrieve_profile_by_id(address, Some(profile_key))
525 .await?;
526
527 let profile_cipher = ProfileCipher::new(profile_key);
528 Ok(profile_cipher.decrypt(encrypted_profile)?)
529 }
530
531 pub async fn upload_versioned_profile<
537 's,
538 C: std::io::Read + Send + 's,
539 R: Rng + CryptoRng,
540 S: AsRef<str>,
541 >(
542 &mut self,
543 aci: libsignal_protocol::Aci,
544 name: ProfileName<S>,
545 about: Option<String>,
546 about_emoji: Option<String>,
547 avatar: AvatarWrite<&'s mut C>,
548 csprng: &mut R,
549 ) -> Result<Option<String>, ProfileManagerError> {
550 let profile_key =
551 self.profile_key.expect("set profile key in AccountManager");
552 let profile_cipher = ProfileCipher::new(profile_key);
553
554 let name = profile_cipher.encrypt_name(name.as_ref(), csprng)?;
556 let about = about.unwrap_or_default();
557 let about = profile_cipher.encrypt_about(about, csprng)?;
558 let about_emoji = about_emoji.unwrap_or_default();
559 let about_emoji = profile_cipher.encrypt_emoji(about_emoji, csprng)?;
560
561 if matches!(avatar, AvatarWrite::NewAvatar(_)) {
563 unimplemented!("Setting avatar requires ProfileCipherStream")
566 }
567
568 let profile_key = profile_cipher.into_inner();
569 let commitment = profile_key.get_commitment(aci);
570 let profile_key_version = profile_key.get_profile_key_version(aci);
571
572 Ok(self
573 .websocket
574 .write_profile::<C, S>(
575 &profile_key_version,
576 &name,
577 &about,
578 &about_emoji,
579 &commitment,
580 avatar,
581 )
582 .await?)
583 }
584
585 pub async fn set_account_attributes(
589 &mut self,
590 attributes: AccountAttributes,
591 ) -> Result<(), ServiceError> {
592 self.websocket.set_account_attributes(attributes).await
593 }
594
595 pub async fn update_device_name<R: Rng + CryptoRng>(
597 &mut self,
598 device_id: libsignal_core::DeviceId,
599 device_name: &str,
600 aci: Aci,
601 aci_identity_store: &dyn IdentityKeyStore,
602 csprng: &mut R,
603 ) -> Result<(), ServiceError> {
604 let addr = aci.to_protocol_address(device_id).unwrap();
605 let public_key = aci_identity_store.get_identity(&addr).await?;
606 let Some(public_key) = public_key else {
607 return Err(ServiceError::SendError {
608 reason: format!("public key for device {addr:?} not found"),
609 });
610 };
611 let encrypted_device_name =
612 encrypt_device_name(csprng, device_name, &public_key)?;
613
614 #[derive(Serialize)]
615 #[serde(rename_all = "camelCase")]
616 struct Data {
617 #[serde(with = "serde_base64")]
618 device_name: Vec<u8>,
619 }
620
621 self.service
622 .request(
623 Method::PUT,
624 Endpoint::service(format!(
625 "/v1/accounts/name?deviceId={}",
626 device_id
627 )),
628 HttpAuthOverride::NoOverride,
629 )?
630 .json(&Data {
631 device_name: encrypted_device_name.encode_to_vec(),
632 })
633 .send()
634 .await?
635 .service_error_for_status()
636 .await?;
637
638 Ok(())
639 }
640
641 pub async fn submit_recaptcha_challenge(
648 &mut self,
649 token: &str,
650 captcha: &str,
651 ) -> Result<(), ServiceError> {
652 self.service
653 .request(
654 Method::PUT,
655 Endpoint::service("/v1/challenge"),
656 HttpAuthOverride::NoOverride,
657 )?
658 .json(&CaptchaAttributes {
659 challenge_type: "captcha",
660 token,
661 captcha,
662 })
663 .send()
664 .await?
665 .service_error_for_status()
666 .await?;
667
668 Ok(())
669 }
670
671 #[tracing::instrument(skip(self, aci_protocol_store, pni_protocol_store, sender, local_aci, csprng), fields(local_aci = local_aci.service_id_string()))]
677 pub async fn pnp_initialize_devices<
678 R: Rng + CryptoRng,
679 AciStore: PreKeysStore + SessionStoreExt,
680 PniStore: PreKeysStore,
681 AciOrPni: ProtocolStore + SenderKeyStore + SessionStoreExt + Sync + Clone,
682 >(
683 &mut self,
684 aci_protocol_store: &mut AciStore,
685 pni_protocol_store: &mut PniStore,
686 mut sender: MessageSender<AciOrPni>,
687 local_aci: Aci,
688 e164: E164,
689 csprng: &mut R,
690 ) -> Result<(), MessageSenderError> {
691 let pni_identity_key_pair =
692 pni_protocol_store.get_identity_key_pair().await?;
693
694 let pni_identity_key = pni_identity_key_pair.identity_key();
695
696 let local_device_ids = aci_protocol_store
698 .get_sub_device_sessions(&local_aci.into())
699 .await?;
700
701 let mut device_messages =
702 Vec::<OutgoingPushMessage>::with_capacity(local_device_ids.len());
703 let mut device_pni_signed_prekeys =
704 HashMap::<String, SignedPreKeyEntity>::with_capacity(
705 local_device_ids.len(),
706 );
707 let mut device_pni_last_resort_kyber_prekeys =
708 HashMap::<String, KyberPreKeyEntity>::with_capacity(
709 local_device_ids.len(),
710 );
711 let mut pni_registration_ids =
712 HashMap::<String, u32>::with_capacity(local_device_ids.len());
713
714 let signature_valid_on_each_signed_pre_key = true;
715 for local_device_id in
716 std::iter::once(*DEFAULT_DEVICE_ID).chain(local_device_ids)
717 {
718 let local_protocol_address =
719 local_aci.to_protocol_address(local_device_id)?;
720 let span = tracing::trace_span!(
721 "filtering devices",
722 address = %local_protocol_address
723 );
724 if (local_device_id != *DEFAULT_DEVICE_ID)
726 && aci_protocol_store
727 .load_session(&local_protocol_address)
728 .instrument(span)
729 .await?
730 .is_none()
731 {
732 tracing::warn!(
733 "No session with device {}, skipping PNI provisioning",
734 local_device_id
735 );
736 continue;
737 }
738 let (
739 _pre_keys,
740 signed_pre_key,
741 _kyber_pre_keys,
742 last_resort_kyber_prekey,
743 ) = if local_device_id == *DEFAULT_DEVICE_ID {
744 crate::pre_keys::replenish_pre_keys(
745 pni_protocol_store,
746 csprng,
747 &pni_identity_key_pair,
748 true,
749 0,
750 0,
751 )
752 .await?
753 } else {
754 let signed_pre_key_pair = KeyPair::generate(csprng);
756 let signed_pre_key_public = signed_pre_key_pair.public_key;
757 let signed_pre_key_signature = pni_identity_key_pair
758 .private_key()
759 .calculate_signature(
760 &signed_pre_key_public.serialize(),
761 csprng,
762 )
763 .map_err(MessageSenderError::InvalidPrivateKey)?;
764
765 let signed_prekey_record = SignedPreKeyRecord::new(
766 csprng.random_range::<u32, _>(0..0xFFFFFF).into(),
767 Timestamp::now(),
768 &signed_pre_key_pair,
769 &signed_pre_key_signature,
770 );
771
772 let kyber_pre_key_record = KyberPreKeyRecord::generate(
774 kem::KeyType::Kyber1024,
775 csprng.random_range::<u32, _>(0..0xFFFFFF).into(),
776 pni_identity_key_pair.private_key(),
777 )?;
778 (
779 vec![],
780 signed_prekey_record,
781 vec![],
782 Some(kyber_pre_key_record),
783 )
784 };
785
786 let registration_id = if local_device_id == *DEFAULT_DEVICE_ID {
787 pni_protocol_store.get_local_registration_id().await?
788 } else {
789 loop {
790 let regid = generate_registration_id(csprng);
791 if !pni_registration_ids.iter().any(|(_k, v)| *v == regid) {
792 break regid;
793 }
794 }
795 };
796
797 let local_device_id_s = local_device_id.to_string();
798 device_pni_signed_prekeys.insert(
799 local_device_id_s.clone(),
800 SignedPreKeyEntity::try_from(&signed_pre_key)?,
801 );
802 device_pni_last_resort_kyber_prekeys.insert(
803 local_device_id_s.clone(),
804 KyberPreKeyEntity::try_from(
805 last_resort_kyber_prekey
806 .as_ref()
807 .expect("requested last resort key"),
808 )?,
809 );
810 pni_registration_ids
811 .insert(local_device_id_s.clone(), registration_id);
812
813 assert!(_pre_keys.is_empty());
814 assert!(_kyber_pre_keys.is_empty());
815
816 if local_device_id == *DEFAULT_DEVICE_ID {
817 continue;
820 }
821 let msg = SyncMessage {
823 pni_change_number: Some(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 padding: Some(random_length_padding(csprng, 512)),
837 ..SyncMessage::default()
838 };
839 let content: ContentBody = msg.into();
840 let msg = sender
841 .create_encrypted_message(
842 &local_aci.into(),
843 None,
844 local_device_id,
845 &content.into_proto().encode_to_vec(),
846 )
847 .await?;
848 device_messages.push(msg);
849 }
850
851 self.websocket
852 .distribute_pni_keys(
853 pni_identity_key,
854 device_messages,
855 device_pni_signed_prekeys,
856 device_pni_last_resort_kyber_prekeys,
857 pni_registration_ids,
858 signature_valid_on_each_signed_pre_key,
859 )
860 .await?;
861
862 Ok(())
863 }
864}
865
866fn calculate_hmac256(
867 mac_key: &[u8],
868 ciphertext: &[u8],
869) -> Result<Output<Hmac<Sha256>>, ServiceError> {
870 let mut mac = Hmac::<Sha256>::new_from_slice(mac_key)
871 .map_err(|_| ServiceError::MacError)?;
872 mac.update(ciphertext);
873 Ok(mac.finalize().into_bytes())
874}
875
876pub fn encrypt_device_name<R: rand::Rng + rand::CryptoRng>(
877 csprng: &mut R,
878 device_name: &str,
879 identity_public: &IdentityKey,
880) -> Result<DeviceName, ServiceError> {
881 let plaintext = device_name.as_bytes().to_vec();
882 let ephemeral_key_pair = KeyPair::generate(csprng);
883
884 let master_secret = ephemeral_key_pair
885 .private_key
886 .calculate_agreement(identity_public.public_key())?;
887
888 let key1 = calculate_hmac256(&master_secret, b"auth")?;
889 let synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
890 let synthetic_iv = &synthetic_iv[..16];
891
892 let key2 = calculate_hmac256(&master_secret, b"cipher")?;
893 let cipher_key = calculate_hmac256(&key2, synthetic_iv)?;
894
895 let mut ciphertext = plaintext;
896
897 const IV: [u8; 16] = [0; 16];
898 let mut cipher = Aes256Ctr128BE::new(&cipher_key, &IV.into());
899 cipher.apply_keystream(&mut ciphertext);
900
901 let device_name = DeviceName {
902 ephemeral_public: Some(
903 ephemeral_key_pair.public_key.serialize().to_vec(),
904 ),
905 synthetic_iv: Some(synthetic_iv.to_vec()),
906 ciphertext: Some(ciphertext),
907 };
908
909 Ok(device_name)
910}
911
912fn decrypt_device_name_from_device_info(
913 string: &str,
914 aci: &IdentityKeyPair,
915) -> Result<String, ServiceError> {
916 let Ok(data) = BASE64_RELAXED.decode(string) else {
917 tracing::trace!(
918 "Device name not base64 encoded, assuming plaintext: {}",
919 string.to_string()
920 );
921 return Ok(string.to_string());
922 };
923 let name = match DeviceName::decode(data.as_slice()) {
924 Ok(decoded) => decoded,
925 Err(_) => {
926 tracing::trace!(
927 "Decoding device name failed, assuming plaintext: {}",
928 string.to_string()
929 );
930 return Ok(string.to_string());
931 },
932 };
933 crate::decrypt_device_name(aci.private_key(), &name)
934}
935
936fn decrypt_device_created_at_from_device_info(
938 id: DeviceId,
939 registration_id: i32,
940 string: &str,
941 aci: &IdentityKeyPair,
942) -> Result<chrono::DateTime<chrono::Utc>, ServiceError> {
943 use signal_crypto::SimpleHpkeReceiver;
944
945 let mut associated_data = [0; 5];
946 associated_data[0] = id.into();
947 associated_data[1..].copy_from_slice(®istration_id.to_be_bytes());
948
949 let data = BASE64_RELAXED.decode(string)?;
950
951 let result =
952 aci.private_key()
953 .open(b"deviceCreatedAt", &associated_data, &data)?;
954
955 let timestamp = i64::from_be_bytes(result.try_into().map_err(|_| {
956 ServiceError::DecryptDeviceInfoFieldError("created-at")
957 })?);
958
959 chrono::DateTime::<chrono::Utc>::from_timestamp_millis(timestamp)
960 .ok_or(ServiceError::DecryptDeviceInfoFieldError("created-at"))
961}
962
963pub fn decrypt_device_name(
964 private_key: &PrivateKey,
965 device_name: &DeviceName,
966) -> Result<String, ServiceError> {
967 let DeviceName {
968 ephemeral_public: Some(ephemeral_public),
969 synthetic_iv: Some(synthetic_iv),
970 ciphertext: Some(ciphertext),
971 } = device_name
972 else {
973 return Err(ServiceError::DecryptDeviceInfoFieldError("name"));
974 };
975
976 let synthetic_iv: [u8; 16] = synthetic_iv[..synthetic_iv.len().min(16)]
977 .try_into()
978 .map_err(|_| ServiceError::MacError)?;
979
980 let ephemeral_public = PublicKey::deserialize(ephemeral_public)?;
981
982 let master_secret = private_key.calculate_agreement(&ephemeral_public)?;
983 let key2 = calculate_hmac256(&master_secret, b"cipher")?;
984 let cipher_key = calculate_hmac256(&key2, &synthetic_iv)?;
985
986 let mut plaintext = ciphertext.to_vec();
987 const IV: [u8; 16] = [0; 16];
988 let mut cipher =
989 Aes256Ctr128BE::new(cipher_key.as_slice().into(), &IV.into());
990 cipher.apply_keystream(&mut plaintext);
991
992 let key1 = calculate_hmac256(&master_secret, b"auth")?;
993 let our_synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
994 let our_synthetic_iv = &our_synthetic_iv[..16];
995
996 if synthetic_iv != our_synthetic_iv {
997 Err(ServiceError::MacError)
998 } else {
999 Ok(String::from_utf8_lossy(&plaintext).to_string())
1000 }
1001}
1002
1003#[cfg(test)]
1004mod tests {
1005 use crate::utils::BASE64_RELAXED;
1006 use base64::Engine;
1007 use libsignal_protocol::{IdentityKeyPair, PrivateKey, PublicKey};
1008
1009 use super::DeviceName;
1010
1011 #[test]
1012 fn encrypt_device_name() -> anyhow::Result<()> {
1013 let input_device_name = "Nokia 3310 Millenial Edition";
1014 let mut csprng = rand::rng();
1015 let identity = IdentityKeyPair::generate(&mut csprng);
1016
1017 let device_name = super::encrypt_device_name(
1018 &mut csprng,
1019 input_device_name,
1020 identity.identity_key(),
1021 )?;
1022
1023 let decrypted_device_name =
1024 super::decrypt_device_name(identity.private_key(), &device_name)?;
1025
1026 assert_eq!(input_device_name, decrypted_device_name);
1027
1028 Ok(())
1029 }
1030
1031 #[test]
1032 fn decrypt_device_name() -> anyhow::Result<()> {
1033 let ephemeral_private_key = PrivateKey::deserialize(
1034 &BASE64_RELAXED
1035 .decode("0CgxHjwwblXjvX8sD5wZDWdYToMRf+CZSlgaUrxCGVo=")?,
1036 )?;
1037 let ephemeral_public_key = PublicKey::deserialize(
1038 &BASE64_RELAXED
1039 .decode("BcZS+Lt6yAKbEpXnRX+I5wHqesuvu93Q2V+fjidwW8R6")?,
1040 )?;
1041
1042 let device_name = DeviceName {
1043 ephemeral_public: Some(ephemeral_public_key.serialize().to_vec()),
1044 synthetic_iv: Some(
1045 BASE64_RELAXED.decode("86gekHGmltnnZ9QARhiFcg==")?,
1046 ),
1047 ciphertext: Some(
1048 BASE64_RELAXED
1049 .decode("MtJ9/9KBWLBVAxfZJD4pLKzP4q+iodRJeCc+/A==")?,
1050 ),
1051 };
1052
1053 let decrypted_device_name =
1054 super::decrypt_device_name(&ephemeral_private_key, &device_name)?;
1055
1056 assert_eq!(decrypted_device_name, "Nokia 3310 Millenial Edition");
1057
1058 Ok(())
1059 }
1060}