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}
104
105impl ProvisioningError {
106 pub fn invalid_public_key(e: impl Into<InvalidKeyError>) -> Self {
107 ProvisioningError::InvalidPublicKey(e.into())
108 }
109
110 pub fn invalid_private_key(e: impl Into<InvalidKeyError>) -> Self {
111 ProvisioningError::InvalidPrivateKey(e.into())
112 }
113}
114
115#[derive(Debug, thiserror::Error)]
116pub enum InvalidKeyError {
117 #[error("curve error: {0}")]
118 Curve(#[from] CurveError),
119 #[error("base64 decoding error: {0}")]
120 Base64(#[from] base64::DecodeError),
121 #[error("protocol error: {0}")]
122 Protocol(#[from] libsignal_protocol::SignalProtocolError),
123}
124
125pub fn generate_registration_id<R: rand::Rng + rand::CryptoRng>(
126 csprng: &mut R,
127) -> u32 {
128 csprng.random_range(1..16380)
129}
130
131#[derive(Debug, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct ConfirmCodeResponse {
134 pub uuid: Uuid,
135 pub storage_capable: bool,
136}
137
138#[derive(Debug)]
139#[allow(clippy::large_enum_variant)]
140pub enum SecondaryDeviceProvisioning {
141 Url(Url),
142 NewDeviceRegistration(NewDeviceRegistration),
143}
144
145#[derive(derive_more::Debug)]
146pub struct NewDeviceRegistration {
147 pub phone_number: libsignal_core::E164,
148 pub device_id: DeviceId,
149 pub registration_id: u32,
150 pub pni_registration_id: u32,
151 pub service_ids: ServiceIds,
152 #[debug(ignore)]
153 pub aci_private_key: PrivateKey,
154 pub aci_public_key: IdentityKey,
155 #[debug(ignore)]
156 pub pni_private_key: PrivateKey,
157 pub pni_public_key: IdentityKey,
158 #[debug(ignore)]
159 pub profile_key: ProfileKey,
160 #[debug(ignore)]
165 pub account_entropy_pool: Option<AccountEntropyPool>,
166}
167
168pub async fn link_device<
169 R: rand::Rng + rand::CryptoRng,
170 Aci: PreKeysStore,
171 Pni: PreKeysStore,
172>(
173 aci_store: &mut Aci,
174 pni_store: &mut Pni,
175 csprng: &mut R,
176 mut push_service: PushService,
177 password: &str,
178 device_name: &str,
179 mut tx: Sender<SecondaryDeviceProvisioning>,
180) -> Result<(), ProvisioningError> {
181 let ws = push_service
183 .ws(
184 "/v1/websocket/provisioning/",
185 "/v1/keepalive/provisioning",
186 &[],
187 None,
188 )
189 .await?;
190
191 let registration_id = csprng.random_range(1..256);
192 let pni_registration_id = csprng.random_range(1..256);
193
194 let provisioning_pipe = ProvisioningPipe::from_socket(ws, csprng);
195 let provision_stream = provisioning_pipe.stream();
196 pin_mut!(provision_stream);
197
198 if let ProvisioningStep::Url(url) = provision_stream
199 .next()
200 .await
201 .ok_or(ProvisioningError::MissingUrl)??
202 {
203 tx.send(SecondaryDeviceProvisioning::Url(url))
204 .await
205 .expect("failed to send provisioning Url in channel");
206 } else {
207 return Err(ProvisioningError::InvalidStep);
208 }
209
210 if let ProvisioningStep::Message(message) =
211 provision_stream
212 .next()
213 .await
214 .ok_or(ProvisioningError::MissingMessage)??
215 {
216 let aci_public_key = PublicKey::deserialize(
217 &message
218 .aci_identity_key_public
219 .ok_or(ProvisioningError::MissingPublicKey)?,
220 )
221 .map_err(ProvisioningError::invalid_public_key)?;
222 let aci_public_key = IdentityKey::new(aci_public_key);
223
224 let aci_private_key = PrivateKey::deserialize(
225 &message
226 .aci_identity_key_private
227 .ok_or(ProvisioningError::MissingPrivateKey)?,
228 )
229 .map_err(ProvisioningError::invalid_private_key)?;
230
231 let pni_public_key = PublicKey::deserialize(
232 &message
233 .pni_identity_key_public
234 .ok_or(ProvisioningError::MissingPublicKey)?,
235 )
236 .map_err(ProvisioningError::invalid_public_key)?;
237 let pni_public_key = IdentityKey::new(pni_public_key);
238
239 let pni_private_key = PrivateKey::deserialize(
240 &message
241 .pni_identity_key_private
242 .ok_or(ProvisioningError::MissingPrivateKey)?,
243 )
244 .map_err(ProvisioningError::invalid_private_key)?;
245
246 let profile_key = message
247 .profile_key
248 .ok_or(ProvisioningError::MissingProfileKey)?;
249
250 let account_entropy_pool = message
254 .account_entropy_pool
255 .map(|s| s.parse())
256 .transpose()?;
257
258 let phone_number = message
259 .number
260 .ok_or(ProvisioningError::MissingPhoneNumber)?;
261
262 let phone_number = phone_number
263 .parse::<E164>()
264 .map_err(ProvisioningError::InvalidPhoneNumber)?;
265
266 let provisioning_code = message
267 .provisioning_code
268 .ok_or(ProvisioningError::MissingProvisioningCode)?;
269
270 let aci_key_pair =
271 IdentityKeyPair::new(aci_public_key, aci_private_key);
272 let pni_key_pair =
273 IdentityKeyPair::new(pni_public_key, pni_private_key);
274
275 let (
276 _aci_pre_keys,
277 aci_signed_pre_key,
278 _aci_pq_pre_keys,
279 aci_pq_last_resort_pre_key,
280 ) = crate::pre_keys::replenish_pre_keys(
281 aci_store,
282 csprng,
283 &aci_key_pair,
284 true,
285 0,
286 0,
287 )
288 .await?;
289
290 let aci_pq_last_resort_pre_key =
291 aci_pq_last_resort_pre_key.expect("requested last resort key");
292 assert!(_aci_pre_keys.is_empty());
293 assert!(_aci_pq_pre_keys.is_empty());
294
295 let (
296 _pni_pre_keys,
297 pni_signed_pre_key,
298 _pni_pq_pre_keys,
299 pni_pq_last_resort_pre_key,
300 ) = crate::pre_keys::replenish_pre_keys(
301 pni_store,
302 csprng,
303 &pni_key_pair,
304 true,
305 0,
306 0,
307 )
308 .await?;
309
310 let pni_pq_last_resort_pre_key =
311 pni_pq_last_resort_pre_key.expect("requested last resort key");
312 assert!(_pni_pre_keys.is_empty());
313 assert!(_pni_pq_pre_keys.is_empty());
314
315 let encrypted_device_name = BASE64_RELAXED.encode(
316 encrypt_device_name(csprng, device_name, &aci_public_key)?
317 .encode_to_vec(),
318 );
319
320 let profile_key = ProfileKey::create(
321 profile_key
322 .as_slice()
323 .try_into()
324 .map_err(ProvisioningError::InvalidProfileKey)?,
325 );
326
327 let request = LinkRequest {
328 verification_code: provisioning_code,
329 account_attributes: LinkAccountAttributes {
330 registration_id,
331 pni_registration_id,
332 fetches_messages: true,
333 capabilities: LinkCapabilities::default(),
334 name: encrypted_device_name,
335 },
336 device_activation_request: DeviceActivationRequest {
337 aci_signed_pre_key: aci_signed_pre_key.try_into()?,
338 pni_signed_pre_key: pni_signed_pre_key.try_into()?,
339 aci_pq_last_resort_pre_key: aci_pq_last_resort_pre_key
340 .try_into()?,
341 pni_pq_last_resort_pre_key: pni_pq_last_resort_pre_key
342 .try_into()?,
343 },
344 };
345
346 let LinkResponse {
347 aci,
348 pni,
349 device_id,
350 } = push_service
351 .link_device(
352 &request,
353 HttpAuth {
354 username: phone_number.to_string(),
355 password: password.to_owned(),
356 },
357 )
358 .await?;
359
360 tx.send(SecondaryDeviceProvisioning::NewDeviceRegistration(
361 NewDeviceRegistration {
362 phone_number,
363 service_ids: ServiceIds { aci, pni },
364 device_id,
365 registration_id,
366 pni_registration_id,
367 aci_private_key,
368 aci_public_key,
369 pni_private_key,
370 pni_public_key,
371 profile_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}