Skip to main content

libsignal_service/provisioning/
mod.rs

1mod cipher;
2mod pipe;
3
4use std::array::TryFromSliceError;
5use std::convert::TryInto;
6
7use libsignal_account_keys::{AccountEntropyPool, InvalidAccountEntropyPool};
8
9pub use cipher::ProvisioningCipher;
10
11use base64::Engine;
12use futures::StreamExt;
13use futures::{channel::mpsc::Sender, pin_mut, SinkExt};
14use libsignal_core::curve::CurveError;
15use libsignal_core::E164;
16use libsignal_protocol::{
17    DeviceId, IdentityKey, IdentityKeyPair, PrivateKey, PublicKey,
18};
19use prost::Message;
20use serde::Deserialize;
21use url::Url;
22use uuid::Uuid;
23use zkgroup::profiles::ProfileKey;
24
25use pipe::{ProvisioningPipe, ProvisioningStep};
26
27use crate::prelude::ServiceError;
28use crate::push_service::linking::{
29    LinkAccountAttributes, LinkCapabilities, LinkRequest, LinkResponse,
30};
31use crate::utils::BASE64_RELAXED;
32use crate::websocket::registration::DeviceActivationRequest;
33use crate::{
34    account_manager::encrypt_device_name,
35    pre_keys::PreKeysStore,
36    push_service::{HttpAuth, PushService, ServiceIds},
37};
38
39pub use crate::proto::{
40    ProvisionEnvelope, ProvisionMessage, ProvisioningVersion,
41};
42
43#[derive(thiserror::Error, Debug)]
44pub enum ProvisioningError {
45    #[error("no provisioning URL received")]
46    MissingUrl,
47    #[error("bad version number (unsupported)")]
48    BadVersionNumber,
49    #[error("missing public key")]
50    MissingPublicKey,
51    #[error("missing private key")]
52    MissingPrivateKey,
53    #[error("invalid public key")]
54    InvalidPublicKey(InvalidKeyError),
55    #[error("invalid privat key")]
56    InvalidPrivateKey(InvalidKeyError),
57    #[error("missing UUID")]
58    MissingUuid,
59    #[error("no provisioning message received")]
60    MissingMessage,
61    #[error("missing profile key")]
62    MissingProfileKey,
63    #[error("missing phone number")]
64    MissingPhoneNumber,
65    #[error("invalid phone number: {0}")]
66    InvalidPhoneNumber(<E164 as std::str::FromStr>::Err),
67    #[error("missing provisioning code")]
68    MissingProvisioningCode,
69    #[error("mismatched MAC")]
70    MismatchedMac,
71    #[error("AES CBC padding error: {0}")]
72    AesPaddingError(aes::cipher::block_padding::UnpadError),
73
74    #[error("invalid provisioning step received")]
75    InvalidStep,
76
77    #[error("Protobuf decoding error: {0}")]
78    DecodeError(#[from] prost::DecodeError),
79    #[error("Websocket error: {reason}")]
80    WsError { reason: String },
81    #[error("Websocket closing")]
82    WsClosing,
83    #[error("Service error: {0}")]
84    ServiceError(#[from] ServiceError),
85    #[error("libsignal-protocol error: {0}")]
86    ProtocolError(#[from] libsignal_protocol::SignalProtocolError),
87    #[error("invalid device ID: {0}")]
88    InvalidDeviceId(#[from] libsignal_core::InvalidDeviceId),
89    #[error("ProvisioningCipher in encrypt-only mode")]
90    EncryptOnlyProvisioningCipher,
91    #[error("invalid profile key bytes")]
92    InvalidProfileKey(TryFromSliceError),
93    #[error("invalid account entropy pool: {0}")]
94    InvalidAccountEntropyPool(#[from] InvalidAccountEntropyPool),
95}
96
97impl ProvisioningError {
98    pub fn invalid_public_key(e: impl Into<InvalidKeyError>) -> Self {
99        ProvisioningError::InvalidPublicKey(e.into())
100    }
101
102    pub fn invalid_private_key(e: impl Into<InvalidKeyError>) -> Self {
103        ProvisioningError::InvalidPrivateKey(e.into())
104    }
105}
106
107#[derive(Debug, thiserror::Error)]
108pub enum InvalidKeyError {
109    #[error("curve error: {0}")]
110    Curve(#[from] CurveError),
111    #[error("base64 decoding error: {0}")]
112    Base64(#[from] base64::DecodeError),
113    #[error("protocol error: {0}")]
114    Protocol(#[from] libsignal_protocol::SignalProtocolError),
115}
116
117pub fn generate_registration_id<R: rand::Rng + rand::CryptoRng>(
118    csprng: &mut R,
119) -> u32 {
120    csprng.random_range(1..16380)
121}
122
123#[derive(Debug, Deserialize)]
124#[serde(rename_all = "camelCase")]
125pub struct ConfirmCodeResponse {
126    pub uuid: Uuid,
127    pub storage_capable: bool,
128}
129
130#[derive(Debug)]
131pub enum SecondaryDeviceProvisioning {
132    Url(Url),
133    NewDeviceRegistration(NewDeviceRegistration),
134}
135
136#[derive(derive_more::Debug)]
137pub struct NewDeviceRegistration {
138    pub phone_number: libsignal_core::E164,
139    pub device_id: DeviceId,
140    pub registration_id: u32,
141    pub pni_registration_id: u32,
142    pub service_ids: ServiceIds,
143    #[debug(ignore)]
144    pub aci_private_key: PrivateKey,
145    pub aci_public_key: IdentityKey,
146    #[debug(ignore)]
147    pub pni_private_key: PrivateKey,
148    pub pni_public_key: IdentityKey,
149    #[debug(ignore)]
150    pub profile_key: ProfileKey,
151    /// Account master key — the deprecated `masterKey` field 13 of
152    /// `ProvisionMessage`. Required by linked devices for legacy state
153    /// (Storage Service, KBS) and still sent by primary devices. May be
154    /// absent if the primary is on a build that has fully migrated to
155    /// `account_entropy_pool` only.
156    #[debug(ignore)]
157    pub master_key: Option<Vec<u8>>,
158    /// Account Entropy Pool — the modern `accountEntropyPool` field 15.
159    /// 64-character alphanumeric string from which the canonical master
160    /// key is derived (`AccountEntropyPool::derive_svr_key`). When present,
161    /// it should be preferred over the deprecated `master_key` field.
162    #[debug(ignore)]
163    pub account_entropy_pool: Option<AccountEntropyPool>,
164}
165
166pub async fn link_device<
167    R: rand::Rng + rand::CryptoRng,
168    Aci: PreKeysStore,
169    Pni: PreKeysStore,
170>(
171    aci_store: &mut Aci,
172    pni_store: &mut Pni,
173    csprng: &mut R,
174    mut push_service: PushService,
175    password: &str,
176    device_name: &str,
177    mut tx: Sender<SecondaryDeviceProvisioning>,
178) -> Result<(), ProvisioningError> {
179    // open a websocket without authentication, to receive a tsurl://
180    let ws = push_service
181        .ws(
182            "/v1/websocket/provisioning/",
183            "/v1/keepalive/provisioning",
184            &[],
185            None,
186        )
187        .await?;
188
189    let registration_id = csprng.random_range(1..256);
190    let pni_registration_id = csprng.random_range(1..256);
191
192    let provisioning_pipe = ProvisioningPipe::from_socket(ws, csprng);
193    let provision_stream = provisioning_pipe.stream();
194    pin_mut!(provision_stream);
195
196    if let ProvisioningStep::Url(url) = provision_stream
197        .next()
198        .await
199        .ok_or(ProvisioningError::MissingUrl)??
200    {
201        tx.send(SecondaryDeviceProvisioning::Url(url))
202            .await
203            .expect("failed to send provisioning Url in channel");
204    } else {
205        return Err(ProvisioningError::InvalidStep);
206    }
207
208    if let ProvisioningStep::Message(message) =
209        provision_stream
210            .next()
211            .await
212            .ok_or(ProvisioningError::MissingMessage)??
213    {
214        let aci_public_key = PublicKey::deserialize(
215            &message
216                .aci_identity_key_public
217                .ok_or(ProvisioningError::MissingPublicKey)?,
218        )
219        .map_err(ProvisioningError::invalid_public_key)?;
220        let aci_public_key = IdentityKey::new(aci_public_key);
221
222        let aci_private_key = PrivateKey::deserialize(
223            &message
224                .aci_identity_key_private
225                .ok_or(ProvisioningError::MissingPrivateKey)?,
226        )
227        .map_err(ProvisioningError::invalid_private_key)?;
228
229        let pni_public_key = PublicKey::deserialize(
230            &message
231                .pni_identity_key_public
232                .ok_or(ProvisioningError::MissingPublicKey)?,
233        )
234        .map_err(ProvisioningError::invalid_public_key)?;
235        let pni_public_key = IdentityKey::new(pni_public_key);
236
237        let pni_private_key = PrivateKey::deserialize(
238            &message
239                .pni_identity_key_private
240                .ok_or(ProvisioningError::MissingPrivateKey)?,
241        )
242        .map_err(ProvisioningError::invalid_private_key)?;
243
244        let profile_key = message
245            .profile_key
246            .ok_or(ProvisioningError::MissingProfileKey)?;
247
248        // Optional in the proto (field 13 deprecated, field 15 modern).
249        // Moved out — partial move, the remaining field accesses below are
250        // independent. presage prefers AEP if both are present.
251        let master_key = message.master_key;
252        let account_entropy_pool = message
253            .account_entropy_pool
254            .map(|s| s.parse())
255            .transpose()?;
256
257        let phone_number = message
258            .number
259            .ok_or(ProvisioningError::MissingPhoneNumber)?;
260
261        let phone_number = phone_number
262            .parse::<E164>()
263            .map_err(ProvisioningError::InvalidPhoneNumber)?;
264
265        let provisioning_code = message
266            .provisioning_code
267            .ok_or(ProvisioningError::MissingProvisioningCode)?;
268
269        let aci_key_pair =
270            IdentityKeyPair::new(aci_public_key, aci_private_key);
271        let pni_key_pair =
272            IdentityKeyPair::new(pni_public_key, pni_private_key);
273
274        let (
275            _aci_pre_keys,
276            aci_signed_pre_key,
277            _aci_pq_pre_keys,
278            aci_pq_last_resort_pre_key,
279        ) = crate::pre_keys::replenish_pre_keys(
280            aci_store,
281            csprng,
282            &aci_key_pair,
283            true,
284            0,
285            0,
286        )
287        .await?;
288
289        let aci_pq_last_resort_pre_key =
290            aci_pq_last_resort_pre_key.expect("requested last resort key");
291        assert!(_aci_pre_keys.is_empty());
292        assert!(_aci_pq_pre_keys.is_empty());
293
294        let (
295            _pni_pre_keys,
296            pni_signed_pre_key,
297            _pni_pq_pre_keys,
298            pni_pq_last_resort_pre_key,
299        ) = crate::pre_keys::replenish_pre_keys(
300            pni_store,
301            csprng,
302            &pni_key_pair,
303            true,
304            0,
305            0,
306        )
307        .await?;
308
309        let pni_pq_last_resort_pre_key =
310            pni_pq_last_resort_pre_key.expect("requested last resort key");
311        assert!(_pni_pre_keys.is_empty());
312        assert!(_pni_pq_pre_keys.is_empty());
313
314        let encrypted_device_name = BASE64_RELAXED.encode(
315            encrypt_device_name(csprng, device_name, &aci_public_key)?
316                .encode_to_vec(),
317        );
318
319        let profile_key = ProfileKey::create(
320            profile_key
321                .as_slice()
322                .try_into()
323                .map_err(ProvisioningError::InvalidProfileKey)?,
324        );
325
326        let request = LinkRequest {
327            verification_code: provisioning_code,
328            account_attributes: LinkAccountAttributes {
329                registration_id,
330                pni_registration_id,
331                fetches_messages: true,
332                capabilities: LinkCapabilities::default(),
333                name: encrypted_device_name,
334            },
335            device_activation_request: DeviceActivationRequest {
336                aci_signed_pre_key: aci_signed_pre_key.try_into()?,
337                pni_signed_pre_key: pni_signed_pre_key.try_into()?,
338                aci_pq_last_resort_pre_key: aci_pq_last_resort_pre_key
339                    .try_into()?,
340                pni_pq_last_resort_pre_key: pni_pq_last_resort_pre_key
341                    .try_into()?,
342            },
343        };
344
345        let LinkResponse {
346            aci,
347            pni,
348            device_id,
349        } = push_service
350            .link_device(
351                &request,
352                HttpAuth {
353                    username: phone_number.to_string(),
354                    password: password.to_owned(),
355                },
356            )
357            .await?;
358
359        tx.send(SecondaryDeviceProvisioning::NewDeviceRegistration(
360            NewDeviceRegistration {
361                phone_number,
362                service_ids: ServiceIds { aci, pni },
363                device_id,
364                registration_id,
365                pni_registration_id,
366                aci_private_key,
367                aci_public_key,
368                pni_private_key,
369                pni_public_key,
370                profile_key,
371                master_key,
372                account_entropy_pool,
373            },
374        ))
375        .await
376        .expect("failed to send provisioning message in rx channel");
377    } else {
378        return Err(ProvisioningError::InvalidStep);
379    }
380
381    Ok(())
382}