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