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