libsignal_service/websocket/
registration.rs1use libsignal_protocol::IdentityKey;
2use reqwest::Method;
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use super::ServiceError;
7use crate::{
8 pre_keys::{KyberPreKeyEntity, SignedPreKeyEntity},
9 utils::serde_base64,
10 websocket::{self, account::AccountAttributes, SignalWebSocket},
11};
12
13#[derive(derive_more::Debug, Clone, Serialize, Deserialize)]
16pub struct AuthCredentials {
17 pub username: String,
18 #[debug(ignore)]
19 pub password: String,
20}
21
22#[derive(Debug, Deserialize)]
23#[serde(rename_all = "camelCase")]
24pub struct RegistrationLockFailure {
25 pub length: Option<u32>,
26 pub time_remaining: Option<u64>,
27 #[serde(rename = "backup_credentials")]
28 pub svr1_credentials: Option<AuthCredentials>,
29 pub svr2_credentials: Option<AuthCredentials>,
30}
31
32#[derive(Debug, Deserialize)]
33#[serde(rename_all = "camelCase")]
34pub struct VerifyAccountResponse {
35 #[serde(rename = "uuid")]
36 pub aci: Uuid,
37 pub pni: Uuid,
38 pub storage_capable: bool,
39 #[serde(default)]
40 pub number: Option<String>,
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
44#[serde(rename_all = "snake_case")]
45pub enum VerificationTransport {
46 Sms,
47 Voice,
48}
49
50#[derive(Clone, Debug)]
51pub enum RegistrationMethod<'a> {
52 SessionId(&'a str),
53 RecoveryPassword(&'a str),
54}
55
56impl<'a> RegistrationMethod<'a> {
57 pub fn session_id(&'a self) -> Option<&'a str> {
58 match self {
59 Self::SessionId(x) => Some(x),
60 _ => None,
61 }
62 }
63
64 pub fn recovery_password(&'a self) -> Option<&'a str> {
65 match self {
66 Self::RecoveryPassword(x) => Some(x),
67 _ => None,
68 }
69 }
70}
71
72#[derive(Debug, Serialize)]
73#[serde(rename_all = "camelCase")]
74pub struct DeviceActivationRequest {
75 pub aci_signed_pre_key: SignedPreKeyEntity,
76 pub pni_signed_pre_key: SignedPreKeyEntity,
77 pub aci_pq_last_resort_pre_key: KyberPreKeyEntity,
78 pub pni_pq_last_resort_pre_key: KyberPreKeyEntity,
79}
80
81#[derive(Debug, Serialize)]
82pub struct CaptchaAttributes<'a> {
83 #[serde(rename = "type")]
84 pub challenge_type: &'a str,
85 pub token: &'a str,
86 pub captcha: &'a str,
87}
88
89#[derive(Debug, Clone, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct RegistrationSessionMetadataResponse {
92 pub id: String,
93 #[serde(default)]
94 pub next_sms: Option<i32>,
95 #[serde(default)]
96 pub next_call: Option<i32>,
97 #[serde(default)]
98 pub next_verification_attempt: Option<i32>,
99 pub allowed_to_request_code: bool,
100 #[serde(default)]
101 pub requested_information: Vec<String>,
102 pub verified: bool,
103}
104
105impl RegistrationSessionMetadataResponse {
106 pub fn push_challenge_required(&self) -> bool {
107 self.requested_information
109 .iter()
110 .any(|x| x.as_str() == "pushChallenge")
111 }
112
113 pub fn captcha_required(&self) -> bool {
114 self.requested_information
116 .iter()
117 .any(|x| x.as_str() == "captcha")
118 }
119}
120
121impl SignalWebSocket<websocket::Unidentified> {
122 pub async fn create_verification_session<'a>(
125 &mut self,
126 number: &'a str,
127 push_token: Option<&'a str>,
128 mcc: Option<&'a str>,
129 mnc: Option<&'a str>,
130 ) -> Result<RegistrationSessionMetadataResponse, ServiceError> {
131 #[derive(serde::Serialize, Debug)]
132 #[serde(rename_all = "camelCase")]
133 struct VerificationSessionMetadataRequestBody<'a> {
134 number: &'a str,
135 push_token: Option<&'a str>,
136 mcc: Option<&'a str>,
137 mnc: Option<&'a str>,
138 push_token_type: Option<&'a str>,
139 }
140
141 self.http_request(Method::POST, "/v1/verification/session")?
142 .send_json(&VerificationSessionMetadataRequestBody {
143 number,
144 push_token_type: push_token.as_ref().map(|_| "fcm"),
145 push_token,
146 mcc,
147 mnc,
148 })
149 .await?
150 .service_error_for_status()
151 .await?
152 .json()
153 .await
154 }
155
156 pub async fn patch_verification_session<'a>(
157 &mut self,
158 session_id: &'a str,
159 push_token: Option<&'a str>,
160 mcc: Option<&'a str>,
161 mnc: Option<&'a str>,
162 captcha: Option<&'a str>,
163 push_challenge: Option<&'a str>,
164 ) -> Result<RegistrationSessionMetadataResponse, ServiceError> {
165 #[derive(serde::Serialize, Debug)]
166 #[serde(rename_all = "camelCase")]
167 struct UpdateVerificationSessionRequestBody<'a> {
168 captcha: Option<&'a str>,
169 push_token: Option<&'a str>,
170 push_challenge: Option<&'a str>,
171 mcc: Option<&'a str>,
172 mnc: Option<&'a str>,
173 push_token_type: Option<&'a str>,
174 }
175
176 self.http_request(
177 Method::PATCH,
178 format!("/v1/verification/session/{}", session_id),
179 )?
180 .send_json(&UpdateVerificationSessionRequestBody {
181 captcha,
182 push_token_type: push_token.as_ref().map(|_| "fcm"),
183 push_token,
184 mcc,
185 mnc,
186 push_challenge,
187 })
188 .await?
189 .service_error_for_status()
190 .await?
191 .json()
192 .await
193 }
194
195 pub async fn request_verification_code(
207 &mut self,
208 session_id: &str,
209 client: &str,
210 transport: VerificationTransport,
214 ) -> Result<RegistrationSessionMetadataResponse, ServiceError> {
215 #[derive(Debug, Serialize)]
216 struct VerificationCodeRequest<'a> {
217 transport: VerificationTransport,
218 client: &'a str,
219 }
220
221 self.http_request(
222 Method::POST,
223 format!("/v1/verification/session/{}/code", session_id),
224 )?
225 .send_json(&VerificationCodeRequest { transport, client })
226 .await?
227 .service_error_for_status()
228 .await?
229 .json()
230 .await
231 }
232}
233
234impl SignalWebSocket<websocket::Identified> {
235 pub async fn submit_registration_request(
236 &mut self,
237 registration_method: RegistrationMethod<'_>,
238 account_attributes: AccountAttributes,
239 skip_device_transfer: bool,
240 aci_identity_key: &IdentityKey,
241 pni_identity_key: &IdentityKey,
242 device_activation_request: DeviceActivationRequest,
243 ) -> Result<VerifyAccountResponse, ServiceError> {
244 #[derive(serde::Serialize, Debug)]
245 #[serde(rename_all = "camelCase")]
246 struct RegistrationSessionRequestBody<'a> {
247 session_id: Option<&'a str>,
254 recovery_password: Option<&'a str>,
255 account_attributes: AccountAttributes,
256 skip_device_transfer: bool,
257 every_signed_key_valid: bool,
258 #[serde(default, with = "serde_base64")]
259 pni_identity_key: Vec<u8>,
260 #[serde(default, with = "serde_base64")]
261 aci_identity_key: Vec<u8>,
262 #[serde(flatten)]
263 device_activation_request: DeviceActivationRequest,
264 }
265
266 self.http_request(Method::POST, "/v1/registration")?
267 .send_json(&RegistrationSessionRequestBody {
268 session_id: registration_method.session_id(),
269 recovery_password: registration_method.recovery_password(),
270 account_attributes,
271 skip_device_transfer,
272 aci_identity_key: aci_identity_key.serialize().into(),
273 pni_identity_key: pni_identity_key.serialize().into(),
274 device_activation_request,
275 every_signed_key_valid: true,
276 })
277 .await?
278 .service_error_for_status()
279 .await?
280 .json()
281 .await
282 }
283
284 pub async fn submit_verification_code(
285 &mut self,
286 session_id: &str,
287 verification_code: &str,
288 ) -> Result<RegistrationSessionMetadataResponse, ServiceError> {
289 #[derive(Debug, Serialize)]
290 struct VerificationCode<'a> {
291 code: &'a str,
292 }
293
294 self.http_request(
295 Method::PUT,
296 format!("/v1/verification/session/{}/code", session_id),
297 )?
298 .send_json(&VerificationCode {
299 code: verification_code,
300 })
301 .await?
302 .service_error_for_status()
303 .await?
304 .json()
305 .await
306 }
307}