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