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