libsignal_service/
account_manager.rs

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        // XXX: should we instead use the `load_last_resort_kyber_pre_keys` method? Or refactor
110        //      those whole traits?
111        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        // `SHA256(identityKeyBytes || signedEcPreKeyId || signedEcPreKeyIdBytes || lastResortKeyId || lastResortKeyBytes)`
124        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    /// Checks the availability of pre-keys, and updates them as necessary.
143    ///
144    /// Parameters are the protocol's `StoreContext`, and the offsets for the next pre-key and
145    /// signed pre-keys.
146    ///
147    /// Equivalent to Java's RefreshPreKeysJob
148    #[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                // Additionally, the second PUT request will fail if this really comes down to an
169                // authorization failure.
170                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        // XXX We should honestly compare the pre-key count with the number of pre-keys we have
195        // locally. If we have more than the server, we should upload them.
196        // Currently the trait doesn't allow us to do that, so we just upload the batch size and
197        // pray.
198        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        // XXX: Maybe this check should be done in the generate_pre_keys function?
227        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    /// Link a new device, given a tsurl.
347    ///
348    /// Equivalent of Java's `AccountManager::addDevice()`
349    ///
350    /// When calling this, make sure that UnidentifiedDelivery is disabled, ie., that your
351    /// application does not send any unidentified messages before linking is complete.
352    /// Cfr.:
353    /// - `app/src/main/java/org/thoughtcrime/securesms/migrations/LegacyMigrationJob.java`:250 and;
354    /// - `app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java`:195
355    ///
356    /// ```java
357    /// TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false);
358    /// ```
359    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            // CURRENT is not exposed by prost :(
415            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    /// Upload a profile
549    ///
550    /// Panics if no `profile_key` was set.
551    ///
552    /// Convenience method for
553    /// ```ignore
554    /// manager.upload_versioned_profile::<std::io::Cursor<Vec<u8>>, _>(uuid, name, about, about_emoji, _)
555    /// ```
556    /// in which the `retain_avatar` parameter sets whether to remove (`false`) or retain (`true`) the
557    /// currently set avatar.
558    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    /// Upload a profile
603    ///
604    /// Panics if no `profile_key` was set.
605    ///
606    /// Returns the avatar url path.
607    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        // Profile encryption
626        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 avatar -> upload
633        if matches!(avatar, AvatarWrite::NewAvatar(_)) {
634            // FIXME ProfileCipherOutputStream.java
635            // It's just AES GCM, but a bit of work to decently implement it with a stream.
636            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    /// Set profile attributes
657    ///
658    /// Signal Android does not allow unsetting voice/video.
659    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    /// Update (encrypted) device name
667    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    /// Upload a proof-required reCaptcha token and response.
713    ///
714    /// Token gotten originally with HTTP status 428 response to sending a message.
715    /// Captcha gotten from user completing the challenge captcha.
716    ///
717    /// It's either a silent OK, or throws a ServiceError.
718    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    /// Initialize PNI on linked devices.
743    ///
744    /// Should be called as the primary device to migrate from pre-PNI to PNI.
745    ///
746    /// This is the equivalent of Android's PnpInitializeDevicesJob or iOS' PniHelloWorldManager.
747    #[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        // For every linked device, we generate a new set of pre-keys, and send them to the device.
768        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            // Skip if we don't have a session with the device
796            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                // Generate a signed prekey
826                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                // Generate a last-resort Kyber prekey
844                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                // This is the primary device
889                // We don't need to send a message to the primary device
890                continue;
891            }
892            // cfr. SignalServiceMessageSender::getEncryptedSyncPniInitializeDeviceMessage
893            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}