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