1use std::fmt;
12use std::time::{SystemTime, UNIX_EPOCH};
13
14use ferogram_crypto::{AuthKey, aes, check_p_and_g, factorize, generate_key_data_from_nonce, rsa};
15use ferogram_tl_types::{Cursor, Deserializable, Serializable};
16use num_bigint::BigUint;
17use sha1::{Digest, Sha1};
18
19fn tl_serialize_bytes(v: &[u8]) -> Vec<u8> {
27 let len = v.len();
28 let mut out = Vec::new();
29 if len < 254 {
30 out.push(len as u8);
31 out.extend_from_slice(v);
32 let total = 1 + len;
33 let pad = (4 - total % 4) % 4;
34 out.extend(std::iter::repeat_n(0u8, pad));
35 } else {
36 out.push(0xfe);
37 out.push((len & 0xff) as u8);
38 out.push(((len >> 8) & 0xff) as u8);
39 out.push(((len >> 16) & 0xff) as u8);
40 out.extend_from_slice(v);
41 let total = 4 + len;
42 let pad = (4 - total % 4) % 4;
43 out.extend(std::iter::repeat_n(0u8, pad));
44 }
45 out
46}
47
48fn serialize_pq_inner_data_dc(
52 pq: &[u8],
53 p: &[u8],
54 q: &[u8],
55 nonce: &[u8; 16],
56 server_nonce: &[u8; 16],
57 new_nonce: &[u8; 32],
58 dc_id: i32,
59) -> Vec<u8> {
60 let mut out = Vec::new();
61 out.extend_from_slice(&0xa9f55f95_u32.to_le_bytes());
63 out.extend(tl_serialize_bytes(pq));
64 out.extend(tl_serialize_bytes(p));
65 out.extend(tl_serialize_bytes(q));
66 out.extend_from_slice(nonce);
67 out.extend_from_slice(server_nonce);
68 out.extend_from_slice(new_nonce);
69 out.extend_from_slice(&dc_id.to_le_bytes());
70 out
71}
72
73#[allow(missing_docs)]
75#[derive(Clone, Debug, PartialEq)]
76pub enum Error {
77 InvalidNonce {
78 got: [u8; 16],
79 expected: [u8; 16],
80 },
81 InvalidPqSize {
82 size: usize,
83 },
84 UnknownFingerprints {
85 fingerprints: Vec<i64>,
86 },
87 DhParamsFail,
88 InvalidServerNonce {
89 got: [u8; 16],
90 expected: [u8; 16],
91 },
92 EncryptedResponseNotPadded {
93 len: usize,
94 },
95 InvalidDhInnerData {
96 error: ferogram_tl_types::deserialize::Error,
97 },
98 InvalidDhPrime {
99 source: ferogram_crypto::DhError,
100 },
101 GParameterOutOfRange {
102 value: BigUint,
103 low: BigUint,
104 high: BigUint,
105 },
106 DhGenRetry,
107 DhGenFail,
108 InvalidAnswerHash {
109 got: [u8; 20],
110 expected: [u8; 20],
111 },
112 InvalidNewNonceHash {
113 got: [u8; 16],
114 expected: [u8; 16],
115 },
116}
117
118impl std::error::Error for Error {}
119
120impl fmt::Display for Error {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 Self::InvalidNonce { got, expected } => {
124 write!(f, "nonce mismatch: got {got:?}, expected {expected:?}")
125 }
126 Self::InvalidPqSize { size } => write!(f, "pq size {size} invalid (expected 8)"),
127 Self::UnknownFingerprints { fingerprints } => {
128 write!(f, "no known fingerprint in {fingerprints:?}")
129 }
130 Self::DhParamsFail => write!(f, "server returned DH params failure"),
131 Self::InvalidServerNonce { got, expected } => write!(
132 f,
133 "server_nonce mismatch: got {got:?}, expected {expected:?}"
134 ),
135 Self::EncryptedResponseNotPadded { len } => {
136 write!(f, "encrypted answer len {len} is not 16-byte aligned")
137 }
138 Self::InvalidDhInnerData { error } => {
139 write!(f, "DH inner data deserialization error: {error}")
140 }
141 Self::InvalidDhPrime { source } => {
142 write!(f, "DH prime/generator validation failed: {source}")
143 }
144 Self::GParameterOutOfRange { value, low, high } => {
145 write!(f, "g={value} not in range ({low}, {high})")
146 }
147 Self::DhGenRetry => write!(f, "DH gen retry requested"),
148 Self::DhGenFail => write!(f, "DH gen failed"),
149 Self::InvalidAnswerHash { got, expected } => write!(
150 f,
151 "answer hash mismatch: got {got:?}, expected {expected:?}"
152 ),
153 Self::InvalidNewNonceHash { got, expected } => write!(
154 f,
155 "new nonce hash mismatch: got {got:?}, expected {expected:?}"
156 ),
157 }
158 }
159}
160
161pub struct Step1 {
163 nonce: [u8; 16],
164}
165
166#[derive(Clone)]
168pub struct Step2 {
169 nonce: [u8; 16],
170 server_nonce: [u8; 16],
171 new_nonce: [u8; 32],
172}
173
174#[derive(Clone)]
177pub struct DhParamsForRetry {
178 pub dh_prime: Vec<u8>,
180 pub g: u32,
182 pub g_a: Vec<u8>,
184 pub server_time: i32,
186 pub aes_key: [u8; 32],
188 pub aes_iv: [u8; 32],
190}
191
192pub struct Step3 {
194 nonce: [u8; 16],
195 server_nonce: [u8; 16],
196 new_nonce: [u8; 32],
197 time_offset: i32,
198 auth_key: [u8; 256],
200 pub dh_params: DhParamsForRetry,
203}
204
205pub enum FinishResult {
208 Done(Finished),
210 Retry {
213 retry_id: i64,
215 dh_params: DhParamsForRetry,
217 nonce: [u8; 16],
219 server_nonce: [u8; 16],
221 new_nonce: [u8; 32],
223 },
224}
225
226#[derive(Clone, Debug, PartialEq)]
228pub struct Finished {
229 pub auth_key: [u8; 256],
231 pub time_offset: i32,
233 pub first_salt: i64,
235}
236
237pub fn step1() -> Result<(ferogram_tl_types::functions::ReqPqMulti, Step1), Error> {
239 let mut buf = [0u8; 16];
240 getrandom::getrandom(&mut buf).expect("getrandom");
241 do_step1(&buf)
242}
243
244fn do_step1(random: &[u8; 16]) -> Result<(ferogram_tl_types::functions::ReqPqMulti, Step1), Error> {
245 let nonce = *random;
246 Ok((
247 ferogram_tl_types::functions::ReqPqMulti { nonce },
248 Step1 { nonce },
249 ))
250}
251
252pub fn step2(
258 data: Step1,
259 response: ferogram_tl_types::enums::ResPq,
260 dc_id: i32,
261) -> Result<(ferogram_tl_types::functions::ReqDhParams, Step2), Error> {
262 let mut rnd = [0u8; 256];
263 getrandom::getrandom(&mut rnd).expect("getrandom");
264 do_step2(data, response, &rnd, dc_id)
265}
266
267fn do_step2(
268 data: Step1,
269 response: ferogram_tl_types::enums::ResPq,
270 random: &[u8; 256],
271 dc_id: i32,
272) -> Result<(ferogram_tl_types::functions::ReqDhParams, Step2), Error> {
273 let Step1 { nonce } = data;
274
275 let ferogram_tl_types::enums::ResPq::ResPq(res_pq) = response;
277
278 check_nonce(&res_pq.nonce, &nonce)?;
279
280 if res_pq.pq.len() != 8 {
281 return Err(Error::InvalidPqSize {
282 size: res_pq.pq.len(),
283 });
284 }
285
286 let pq = u64::from_be_bytes(res_pq.pq.as_slice().try_into().unwrap());
287 let (p, q) = factorize(pq);
288
289 let mut new_nonce = [0u8; 32];
290 new_nonce.copy_from_slice(&random[..32]);
291
292 let rnd224: &[u8; 224] = random[32..].try_into().unwrap();
294
295 fn trim_be(v: u64) -> Vec<u8> {
296 let b = v.to_be_bytes();
297 let skip = b.iter().position(|&x| x != 0).unwrap_or(7);
298 b[skip..].to_vec()
299 }
300
301 let p_bytes = trim_be(p);
302 let q_bytes = trim_be(q);
303
304 let pq_inner = serialize_pq_inner_data_dc(
308 &pq.to_be_bytes(),
309 &p_bytes,
310 &q_bytes,
311 &nonce,
312 &res_pq.server_nonce,
313 &new_nonce,
314 dc_id,
315 );
316
317 let fingerprint = res_pq
318 .server_public_key_fingerprints
319 .iter()
320 .copied()
321 .find(|&fp| key_for_fingerprint(fp).is_some())
322 .ok_or_else(|| Error::UnknownFingerprints {
323 fingerprints: res_pq.server_public_key_fingerprints.clone(),
324 })?;
325
326 let key = key_for_fingerprint(fingerprint).unwrap();
327 let ciphertext = rsa::encrypt_hashed(&pq_inner, &key, rnd224);
328
329 Ok((
330 ferogram_tl_types::functions::ReqDhParams {
331 nonce,
332 server_nonce: res_pq.server_nonce,
333 p: p_bytes,
334 q: q_bytes,
335 public_key_fingerprint: fingerprint,
336 encrypted_data: ciphertext,
337 },
338 Step2 {
339 nonce,
340 server_nonce: res_pq.server_nonce,
341 new_nonce,
342 },
343 ))
344}
345
346pub fn step3(
352 data: Step2,
353 response: ferogram_tl_types::enums::ServerDhParams,
354) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
355 let mut rnd = [0u8; 272];
356 getrandom::getrandom(&mut rnd).expect("getrandom");
357 let now = SystemTime::now()
358 .duration_since(UNIX_EPOCH)
359 .unwrap()
360 .as_secs() as i32;
361 do_step3(data, response, &rnd, now, 0)
362}
363
364pub fn retry_step3(
368 dh_params: &DhParamsForRetry,
369 nonce: [u8; 16],
370 server_nonce: [u8; 16],
371 new_nonce: [u8; 32],
372 retry_id: i64,
373) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
374 let mut rnd = [0u8; 272];
375 getrandom::getrandom(&mut rnd).expect("getrandom");
376 let now = SystemTime::now()
377 .duration_since(UNIX_EPOCH)
378 .unwrap()
379 .as_secs() as i32;
380 generate_client_dh_params(
381 dh_params,
382 nonce,
383 server_nonce,
384 new_nonce,
385 retry_id,
386 &rnd,
387 now,
388 )
389}
390
391fn generate_client_dh_params(
392 dh: &DhParamsForRetry,
393 nonce: [u8; 16],
394 server_nonce: [u8; 16],
395 new_nonce: [u8; 32],
396 retry_id: i64,
397 random: &[u8; 272],
398 now: i32,
399) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
400 let dh_prime = BigUint::from_bytes_be(&dh.dh_prime);
401 let g = BigUint::from(dh.g);
402 let g_a = BigUint::from_bytes_be(&dh.g_a);
403 let time_offset = dh.server_time - now;
404
405 let b = BigUint::from_bytes_be(&random[..256]);
406 let g_b = g.modpow(&b, &dh_prime);
407
408 let one = BigUint::from(1u32);
409 let safety = one.clone() << (2048 - 64);
410 check_g_in_range(&g_b, &one, &(&dh_prime - &one))?;
411 check_g_in_range(&g_b, &safety, &(&dh_prime - &safety))?;
412
413 let client_dh_inner = ferogram_tl_types::enums::ClientDhInnerData::ClientDhInnerData(
414 ferogram_tl_types::types::ClientDhInnerData {
415 nonce,
416 server_nonce,
417 retry_id,
418 g_b: g_b.to_bytes_be(),
419 },
420 )
421 .to_bytes();
422
423 let digest: [u8; 20] = {
424 let mut sha = Sha1::new();
425 sha.update(&client_dh_inner);
426 sha.finalize().into()
427 };
428
429 let pad_len = (16 - ((20 + client_dh_inner.len()) % 16)) % 16;
430 let rnd16 = &random[256..256 + pad_len.min(16)];
431
432 let mut hashed = Vec::with_capacity(20 + client_dh_inner.len() + pad_len);
433 hashed.extend_from_slice(&digest);
434 hashed.extend_from_slice(&client_dh_inner);
435 hashed.extend_from_slice(&rnd16[..pad_len]);
436
437 let key: [u8; 32] = dh.aes_key;
438 let iv: [u8; 32] = dh.aes_iv;
439 aes::ige_encrypt(&mut hashed, &key, &iv);
440
441 let mut auth_key_bytes = [0u8; 256];
443 let gab_bytes = g_a.modpow(&b, &dh_prime).to_bytes_be();
444 let skip = 256 - gab_bytes.len();
445 auth_key_bytes[skip..].copy_from_slice(&gab_bytes);
446
447 Ok((
448 ferogram_tl_types::functions::SetClientDhParams {
449 nonce,
450 server_nonce,
451 encrypted_data: hashed,
452 },
453 Step3 {
454 nonce,
455 server_nonce,
456 new_nonce,
457 time_offset,
458 auth_key: auth_key_bytes,
459 dh_params: dh.clone(),
460 },
461 ))
462}
463
464fn do_step3(
465 data: Step2,
466 response: ferogram_tl_types::enums::ServerDhParams,
467 random: &[u8; 272],
468 now: i32,
469 retry_id: i64,
470) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
471 let Step2 {
472 nonce,
473 server_nonce,
474 new_nonce,
475 } = data;
476
477 let mut server_dh_ok = match response {
478 ferogram_tl_types::enums::ServerDhParams::Fail(f) => {
479 check_nonce(&f.nonce, &nonce)?;
480 check_server_nonce(&f.server_nonce, &server_nonce)?;
481 return Err(Error::DhParamsFail);
482 }
483 ferogram_tl_types::enums::ServerDhParams::Ok(x) => x,
484 };
485
486 check_nonce(&server_dh_ok.nonce, &nonce)?;
487 check_server_nonce(&server_dh_ok.server_nonce, &server_nonce)?;
488
489 if server_dh_ok.encrypted_answer.len() % 16 != 0 {
490 return Err(Error::EncryptedResponseNotPadded {
491 len: server_dh_ok.encrypted_answer.len(),
492 });
493 }
494
495 let (key_arr, iv_arr) = generate_key_data_from_nonce(&server_nonce, &new_nonce);
496 aes::ige_decrypt(&mut server_dh_ok.encrypted_answer, &key_arr, &iv_arr);
497 let plain = server_dh_ok.encrypted_answer;
498
499 let got_hash: [u8; 20] = plain[..20].try_into().unwrap();
500 let mut cursor = Cursor::from_slice(&plain[20..]);
501
502 let inner = match ferogram_tl_types::enums::ServerDhInnerData::deserialize(&mut cursor) {
503 Ok(ferogram_tl_types::enums::ServerDhInnerData::ServerDhInnerData(x)) => x,
504 Err(e) => return Err(Error::InvalidDhInnerData { error: e }),
505 };
506
507 let expected_hash: [u8; 20] = {
508 let mut sha = Sha1::new();
509 sha.update(&plain[20..20 + cursor.pos()]);
510 sha.finalize().into()
511 };
512 if got_hash != expected_hash {
513 return Err(Error::InvalidAnswerHash {
514 got: got_hash,
515 expected: expected_hash,
516 });
517 }
518
519 check_nonce(&inner.nonce, &nonce)?;
520 check_server_nonce(&inner.server_nonce, &server_nonce)?;
521
522 check_p_and_g(&inner.dh_prime, inner.g as u32)
523 .map_err(|source| Error::InvalidDhPrime { source })?;
524
525 let dh_prime_bn = BigUint::from_bytes_be(&inner.dh_prime);
527 let one = BigUint::from(1u32);
528 let g_a_bn = BigUint::from_bytes_be(&inner.g_a);
529 let safety = one.clone() << (2048 - 64);
530 check_g_in_range(&g_a_bn, &safety, &(&dh_prime_bn - &safety))?;
531
532 let dh = DhParamsForRetry {
533 dh_prime: inner.dh_prime,
534 g: inner.g as u32,
535 g_a: inner.g_a,
536 server_time: inner.server_time,
537 aes_key: key_arr,
538 aes_iv: iv_arr,
539 };
540
541 generate_client_dh_params(&dh, nonce, server_nonce, new_nonce, retry_id, random, now)
542}
543
544pub fn finish(
551 data: Step3,
552 response: ferogram_tl_types::enums::SetClientDhParamsAnswer,
553) -> Result<FinishResult, Error> {
554 let Step3 {
555 nonce,
556 server_nonce,
557 new_nonce,
558 time_offset,
559 auth_key: auth_key_bytes,
560 dh_params,
561 } = data;
562
563 struct DhData {
564 nonce: [u8; 16],
565 server_nonce: [u8; 16],
566 hash: [u8; 16],
567 num: u8,
568 }
569
570 let dh = match response {
571 ferogram_tl_types::enums::SetClientDhParamsAnswer::DhGenOk(x) => DhData {
573 nonce: x.nonce,
574 server_nonce: x.server_nonce,
575 hash: x.new_nonce_hash1,
576 num: 1,
577 },
578 ferogram_tl_types::enums::SetClientDhParamsAnswer::DhGenRetry(x) => DhData {
579 nonce: x.nonce,
580 server_nonce: x.server_nonce,
581 hash: x.new_nonce_hash2,
582 num: 2,
583 },
584 ferogram_tl_types::enums::SetClientDhParamsAnswer::DhGenFail(x) => DhData {
585 nonce: x.nonce,
586 server_nonce: x.server_nonce,
587 hash: x.new_nonce_hash3,
588 num: 3,
589 },
590 };
591
592 check_nonce(&dh.nonce, &nonce)?;
593 check_server_nonce(&dh.server_nonce, &server_nonce)?;
594
595 let auth_key = AuthKey::from_bytes(auth_key_bytes);
596 let expected_hash = auth_key.calc_new_nonce_hash(&new_nonce, dh.num);
597 check_new_nonce_hash(&dh.hash, &expected_hash)?;
598
599 let first_salt = {
600 let mut buf = [0u8; 8];
601 for ((dst, a), b) in buf.iter_mut().zip(&new_nonce[..8]).zip(&server_nonce[..8]) {
602 *dst = a ^ b;
603 }
604 i64::from_le_bytes(buf)
605 };
606
607 match dh.num {
608 1 => Ok(FinishResult::Done(Finished {
609 auth_key: auth_key.to_bytes(),
610 time_offset,
611 first_salt,
612 })),
613 2 => {
614 let aux_hash: [u8; 20] = {
616 let mut sha = Sha1::new();
617 sha.update(auth_key.to_bytes());
618 sha.finalize().into()
619 };
620 let retry_id = i64::from_le_bytes(aux_hash[..8].try_into().unwrap());
621 Ok(FinishResult::Retry {
622 retry_id,
623 dh_params,
624 nonce,
625 server_nonce,
626 new_nonce,
627 })
628 }
629 _ => Err(Error::DhGenFail),
630 }
631}
632
633fn check_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
634 if got == expected {
635 Ok(())
636 } else {
637 Err(Error::InvalidNonce {
638 got: *got,
639 expected: *expected,
640 })
641 }
642}
643fn check_server_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
644 if got == expected {
645 Ok(())
646 } else {
647 Err(Error::InvalidServerNonce {
648 got: *got,
649 expected: *expected,
650 })
651 }
652}
653fn check_new_nonce_hash(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
654 if got == expected {
655 Ok(())
656 } else {
657 Err(Error::InvalidNewNonceHash {
658 got: *got,
659 expected: *expected,
660 })
661 }
662}
663fn check_g_in_range(val: &BigUint, lo: &BigUint, hi: &BigUint) -> Result<(), Error> {
664 if lo < val && val < hi {
665 Ok(())
666 } else {
667 Err(Error::GParameterOutOfRange {
668 value: val.clone(),
669 low: lo.clone(),
670 high: hi.clone(),
671 })
672 }
673}
674
675#[allow(clippy::unreadable_literal)]
677pub fn key_for_fingerprint(fp: i64) -> Option<rsa::Key> {
678 Some(match fp {
679 -3414540481677951611 => rsa::Key::new(
681 "29379598170669337022986177149456128565388431120058863768162556424047512191330847455146576344487764408661701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531316010870401287642763009136156734339538042419388722777357134487746169093539093850251243897188928735903389451772730245253062963384108812842079887538976360465290946139638691491496062099570836476454855996319192747663615955633778034897140982517446405334423701359108810182097749467210509584293428076654573384828809574217079944388301239431309115013843331317877374435868468779972014486325557807783825502498215169806323",
682 "65537",
683 )?,
684 -5595554452916591101 => rsa::Key::new(
686 "25342889448840415564971689590713473206898847759084779052582026594546022463853940585885215951168491965708222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166848940585952337613333022396096584117954892216031229237302943701877588456738335398602461675225081791820393153757504952636234951323237820036543581047826906120927972487366805292115792231423684261262330394324750785450942589751755390156647751460719351439969059949569615302809050721500330239005077889855323917509948255722081644689442127297605422579707142646660768825302832201908302295573257427896031830742328565032949",
687 "65537",
688 )?,
689 _ => return None,
690 })
691}