1use std::fmt;
38use std::time::{SystemTime, UNIX_EPOCH};
39
40use grammers_crypto::hex;
41use grammers_crypto::{AuthKey, factorize, rsa};
42use grammers_tl_types::{self as tl, Cursor, Deserializable, Serializable};
43use num_bigint::{BigUint, ToBigUint};
44use sha1::{Digest, Sha1};
45
46const TRACE_AUTH_GEN: bool = false;
49
50#[derive(Clone, Debug, PartialEq)]
52pub enum Error {
53 InvalidNonce {
55 got: [u8; 16],
57
58 expected: [u8; 16],
60 },
61
62 InvalidPqSize {
64 size: usize,
66 },
67
68 UnknownFingerprints {
70 fingerprints: Vec<i64>,
72 },
73
74 DhParamsFail,
76
77 InvalidServerNonce {
79 got: [u8; 16],
81
82 expected: [u8; 16],
84 },
85
86 EncryptedResponseNotPadded {
88 len: usize,
90 },
91
92 InvalidDhInnerData {
94 error: tl::deserialize::Error,
96 },
97
98 GParameterOutOfRange {
100 value: BigUint,
101 low: BigUint,
102 high: BigUint,
103 },
104
105 DhGenRetry,
107
108 DhGenFail,
110
111 InvalidAnswerHash {
113 got: [u8; 20],
115
116 expected: [u8; 20],
118 },
119
120 InvalidNewNonceHash {
122 got: [u8; 16],
124
125 expected: [u8; 16],
127 },
128}
129
130impl std::error::Error for Error {}
131
132impl fmt::Display for Error {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 match self {
135 Self::InvalidNonce { got, expected } => {
136 write!(f, "invalid nonce: got {got:?}, expected {expected:?}")
137 }
138 Self::InvalidPqSize { size } => write!(f, "invalid pq size {size}"),
139 Self::UnknownFingerprints { fingerprints } => {
140 write!(f, "all server fingerprints are unknown: {fingerprints:?}")
141 }
142 Self::DhParamsFail => write!(f, "the generation of DH parameters by the server failed"),
143 Self::InvalidServerNonce { got, expected } => write!(
144 f,
145 "invalid server nonce: got {got:?}, expected {expected:?}"
146 ),
147 Self::EncryptedResponseNotPadded { len } => write!(
148 f,
149 "the encrypted server response was {len} bytes long, which is not correctly padded"
150 ),
151 Self::InvalidDhInnerData { error } => {
152 write!(f, "could not deserialize DH inner data: {error}")
153 }
154 Self::GParameterOutOfRange { low, high, value } => write!(
155 f,
156 "the parameter g = {value} was not in the range {low}..{high}"
157 ),
158 Self::DhGenRetry => write!(f, "the generation of DH parameters should be retried"),
159 Self::DhGenFail => write!(f, "the generation of DH parameters failed"),
160 Self::InvalidAnswerHash { got, expected } => {
161 write!(f, "invalid answer hash: got {got:?}, expected {expected:?}")
162 }
163 Self::InvalidNewNonceHash { got, expected } => write!(
164 f,
165 "invalid new nonce hash: got {got:?}, expected {expected:?}"
166 ),
167 }
168 }
169}
170
171pub struct Step1 {
173 nonce: [u8; 16],
174}
175
176pub struct Step2 {
178 nonce: [u8; 16],
179 server_nonce: [u8; 16],
180 new_nonce: [u8; 32],
181}
182
183pub struct Step3 {
185 nonce: [u8; 16],
186 server_nonce: [u8; 16],
187 new_nonce: [u8; 32],
188 gab: BigUint,
189 time_offset: i32,
190}
191
192pub fn step1() -> Result<(tl::functions::ReqPqMulti, Step1), Error> {
194 let random_bytes = {
195 let mut buffer = [0; 16];
196 getrandom::fill(&mut buffer).expect("failed to generate secure data for auth key");
197 buffer
198 };
199
200 if TRACE_AUTH_GEN {
201 println!("r {}", hex::to_hex(&random_bytes));
202 }
203
204 let res = do_step1(&random_bytes);
205 if TRACE_AUTH_GEN {
206 if let Ok((x, _)) = &res {
207 println!("> {}", hex::to_hex(&x.to_bytes()));
208 }
209 }
210 res
211}
212
213fn do_step1(random_bytes: &[u8; 16]) -> Result<(tl::functions::ReqPqMulti, Step1), Error> {
215 let nonce = *random_bytes;
217 Ok((tl::functions::ReqPqMulti { nonce }, Step1 { nonce }))
218}
219
220pub fn step2(
222 data: Step1,
223 response: tl::enums::ResPq,
224) -> Result<(tl::functions::ReqDhParams, Step2), Error> {
225 if TRACE_AUTH_GEN {
226 println!("< {}", hex::to_hex(&response.to_bytes()));
227 }
228
229 let random_bytes = {
230 let mut buffer = [0; 32 + 224];
231 getrandom::fill(&mut buffer).expect("failed to generate secure data for auth key");
232 buffer
233 };
234
235 if TRACE_AUTH_GEN {
236 println!("r {}", hex::to_hex(&random_bytes));
237 }
238
239 let res = do_step2(data, response, &random_bytes);
240 if TRACE_AUTH_GEN {
241 if let Ok((x, _)) = &res {
242 println!("> {}", hex::to_hex(&x.to_bytes()));
243 }
244 }
245 res
246}
247
248fn do_step2(
249 data: Step1,
250 response: tl::enums::ResPq,
251 random_bytes: &[u8; 32 + 224],
252) -> Result<(tl::functions::ReqDhParams, Step2), Error> {
253 let Step1 { nonce } = data;
255 let tl::enums::ResPq::Pq(res_pq) = response;
256
257 check_nonce(&res_pq.nonce, &nonce)?;
258
259 if res_pq.pq.len() != 8 {
260 return Err(Error::InvalidPqSize {
261 size: res_pq.pq.len(),
262 });
263 }
264
265 let pq = {
266 let mut buffer = [0; 8];
267 buffer.copy_from_slice(&res_pq.pq);
268 u64::from_be_bytes(buffer)
269 };
270
271 let (p, q) = factorize(pq);
272 let new_nonce = {
273 let mut buffer = [0; 32];
274 buffer.copy_from_slice(&random_bytes[..32]);
275 buffer
276 };
277
278 let random_bytes = {
280 let mut buffer = [0; 224];
281 buffer.copy_from_slice(&random_bytes[32..]);
282 buffer
283 };
284
285 let p_bytes = {
288 let mut buffer = p.to_be_bytes().to_vec();
289 if let Some(pos) = buffer.iter().position(|&b| b != 0) {
290 buffer = buffer[pos..].to_vec();
291 }
292 buffer
293 };
294 let q_bytes = {
295 let mut buffer = q.to_be_bytes().to_vec();
296 if let Some(pos) = buffer.iter().position(|&b| b != 0) {
297 buffer = buffer[pos..].to_vec();
298 }
299 buffer
300 };
301
302 let pq_inner_data = tl::enums::PQInnerData::Data(tl::types::PQInnerData {
305 pq: pq.to_be_bytes().to_vec(),
306 p: p_bytes.clone(),
307 q: q_bytes.clone(),
308 nonce,
309 server_nonce: res_pq.server_nonce,
310 new_nonce,
311 })
312 .to_bytes();
313
314 let fingerprint = match res_pq
316 .server_public_key_fingerprints
317 .iter()
318 .cloned()
319 .find(|&fingerprint| key_for_fingerprint(fingerprint).is_some())
320 {
321 Some(x) => x,
322 None => {
323 return Err(Error::UnknownFingerprints {
324 fingerprints: res_pq.server_public_key_fingerprints.clone(),
325 });
326 }
327 };
328
329 let key = key_for_fingerprint(fingerprint).unwrap();
331 let ciphertext = rsa::encrypt_hashed(&pq_inner_data, &key, &random_bytes);
332
333 Ok((
334 tl::functions::ReqDhParams {
335 nonce,
336 server_nonce: res_pq.server_nonce,
337 p: p_bytes,
338 q: q_bytes,
339 public_key_fingerprint: fingerprint,
340 encrypted_data: ciphertext,
341 },
342 Step2 {
343 nonce,
344 server_nonce: res_pq.server_nonce,
345 new_nonce,
346 },
347 ))
348}
349
350pub fn step3(
352 data: Step2,
353 response: tl::enums::ServerDhParams,
354) -> Result<(tl::functions::SetClientDhParams, Step3), Error> {
355 if TRACE_AUTH_GEN {
356 println!("< {}", hex::to_hex(&response.to_bytes()));
357 }
358
359 let random_bytes = {
360 let mut buffer = [0; 256 + 16];
361 getrandom::fill(&mut buffer).expect("failed to generate secure data for auth key");
362 buffer
363 };
364
365 if TRACE_AUTH_GEN {
366 println!("r {}", hex::to_hex(&random_bytes));
367 }
368
369 let now = SystemTime::now()
370 .duration_since(UNIX_EPOCH)
371 .expect("system time is before epoch")
372 .as_secs() as i32;
373
374 let res = do_step3(data, response, &random_bytes, now);
375 if TRACE_AUTH_GEN {
376 if let Ok((x, _)) = &res {
377 println!("> {}", hex::to_hex(&x.to_bytes()));
378 }
379 }
380 res
381}
382
383fn do_step3(
384 data: Step2,
385 response: tl::enums::ServerDhParams,
386 random_bytes: &[u8; 256 + 16],
387 now: i32,
388) -> Result<(tl::functions::SetClientDhParams, Step3), Error> {
389 let Step2 {
390 nonce,
391 server_nonce,
392 new_nonce,
393 } = data;
394 let server_dh_params = response;
395
396 let mut server_dh_params = match server_dh_params {
398 tl::enums::ServerDhParams::Fail(server_dh_params) => {
399 check_nonce(&server_dh_params.nonce, &nonce)?;
402 check_server_nonce(&server_dh_params.server_nonce, &server_nonce)?;
403
404 let sha = {
405 let mut hasher = Sha1::new();
406 hasher.update(new_nonce);
407 hasher.finalize()
408 };
409 let new_nonce_hash = {
410 let mut buffer = [0; 16];
411 buffer.copy_from_slice(&sha[4..20]);
412 buffer
413 };
414 check_new_nonce_hash(&server_dh_params.new_nonce_hash, &new_nonce_hash)?;
415
416 return Err(Error::DhParamsFail);
417 }
418 tl::enums::ServerDhParams::Ok(x) => x,
419 };
420
421 check_nonce(&server_dh_params.nonce, &nonce)?;
422 check_server_nonce(&server_dh_params.server_nonce, &server_nonce)?;
423
424 if server_dh_params.encrypted_answer.len() % 16 != 0 {
425 return Err(Error::EncryptedResponseNotPadded {
426 len: server_dh_params.encrypted_answer.len(),
427 });
428 }
429
430 let (key, iv) = grammers_crypto::generate_key_data_from_nonce(&server_nonce, &new_nonce);
432
433 grammers_crypto::aes::ige_decrypt(&mut server_dh_params.encrypted_answer, &key, &iv);
435 let plain_text_answer = server_dh_params.encrypted_answer;
436
437 let got_answer_hash = {
438 let mut buffer = [0; 20];
439 buffer.copy_from_slice(&plain_text_answer[..20]);
440 buffer
441 };
442
443 let mut plain_text_cursor = Cursor::from_slice(&plain_text_answer[20..]);
446 let server_dh_inner = match tl::enums::ServerDhInnerData::deserialize(&mut plain_text_cursor) {
447 Ok(tl::enums::ServerDhInnerData::Data(x)) => x,
448 Err(error) => return Err(Error::InvalidDhInnerData { error }),
449 };
450
451 let expected_answer_hash = {
452 let mut hasher = Sha1::new();
453 hasher.update(&plain_text_answer[20..20 + plain_text_cursor.pos()]);
454 hasher.finalize().into()
455 };
456
457 if got_answer_hash != expected_answer_hash {
458 return Err(Error::InvalidAnswerHash {
459 got: got_answer_hash,
460 expected: expected_answer_hash,
461 });
462 }
463
464 check_nonce(&server_dh_inner.nonce, &nonce)?;
465 check_server_nonce(&server_dh_inner.server_nonce, &server_nonce)?;
466
467 let dh_prime = BigUint::from_bytes_be(&server_dh_inner.dh_prime);
469 let g = server_dh_inner.g.to_biguint().unwrap();
470 let g_a = BigUint::from_bytes_be(&server_dh_inner.g_a);
471
472 let time_offset = server_dh_inner.server_time - now;
473
474 let b = BigUint::from_bytes_be(&random_bytes[..256]);
475 let g_b = g.modpow(&b, &dh_prime);
476 let gab = g_a.modpow(&b, &dh_prime);
477
478 let random_bytes = {
480 let mut buffer = [0u8; 16];
481 buffer.copy_from_slice(&random_bytes[256..]);
482 buffer
483 };
484
485 let one = BigUint::from_bytes_be(&[1]);
492 check_g_in_range(&g, &one, &(&dh_prime - &one))?;
493 check_g_in_range(&g_a, &one, &(&dh_prime - &one))?;
494 check_g_in_range(&g_b, &one, &(&dh_prime - &one))?;
495
496 let safety_range = one << (2048 - 64);
497 check_g_in_range(&g_a, &safety_range, &(&dh_prime - &safety_range))?;
498 check_g_in_range(&g_b, &safety_range, &(&dh_prime - &safety_range))?;
499
500 let client_dh_inner = tl::enums::ClientDhInnerData::Data(tl::types::ClientDhInnerData {
502 nonce,
503 server_nonce,
504 retry_id: 0, g_b: g_b.to_bytes_be(),
506 })
507 .to_bytes();
508
509 let sha = {
511 let mut hasher = Sha1::new();
512 hasher.update(&client_dh_inner);
513 hasher.finalize()
514 };
515
516 let mut client_dh_inner_hashed = {
517 let mut buffer = Vec::with_capacity(20 + client_dh_inner.len() + 16);
518
519 buffer.extend(&sha);
520 buffer.extend(&client_dh_inner);
521
522 let pad_len = (16 - (buffer.len() % 16)) % 16;
525 buffer.extend(&random_bytes[..pad_len]);
526
527 buffer
528 };
529
530 grammers_crypto::aes::ige_encrypt(&mut client_dh_inner_hashed, &key, &iv);
531 let client_dh_encrypted = client_dh_inner_hashed;
532
533 Ok((
534 tl::functions::SetClientDhParams {
535 nonce,
536 server_nonce,
537 encrypted_data: client_dh_encrypted,
538 },
539 Step3 {
540 nonce,
541 server_nonce,
542 new_nonce,
543 gab,
544 time_offset,
545 },
546 ))
547}
548
549#[derive(Clone, Debug, PartialEq)]
551pub struct Finished {
552 pub auth_key: [u8; 256],
553 pub time_offset: i32,
554 pub first_salt: i64,
555}
556
557pub fn create_key(
559 data: Step3,
560 response: tl::enums::SetClientDhParamsAnswer,
561) -> Result<Finished, Error> {
562 if TRACE_AUTH_GEN {
563 println!("< {}", hex::to_hex(&response.to_bytes()));
564 }
565
566 let Step3 {
567 nonce,
568 server_nonce,
569 new_nonce,
570 gab,
571 time_offset,
572 } = data;
573 let dh_gen = response;
574
575 struct DhGenData {
576 nonce: [u8; 16],
577 server_nonce: [u8; 16],
578 new_nonce_hash: [u8; 16],
579 nonce_number: u8,
580 }
581
582 let dh_gen = match dh_gen {
583 tl::enums::SetClientDhParamsAnswer::DhGenOk(x) => DhGenData {
584 nonce: x.nonce,
585 server_nonce: x.server_nonce,
586 new_nonce_hash: x.new_nonce_hash1,
587 nonce_number: 1,
588 },
589 tl::enums::SetClientDhParamsAnswer::DhGenRetry(x) => DhGenData {
590 nonce: x.nonce,
591 server_nonce: x.server_nonce,
592 new_nonce_hash: x.new_nonce_hash2,
593 nonce_number: 2,
594 },
595 tl::enums::SetClientDhParamsAnswer::DhGenFail(x) => DhGenData {
596 nonce: x.nonce,
597 server_nonce: x.server_nonce,
598 new_nonce_hash: x.new_nonce_hash3,
599 nonce_number: 3,
600 },
601 };
602
603 check_nonce(&dh_gen.nonce, &nonce)?;
604 check_server_nonce(&dh_gen.server_nonce, &server_nonce)?;
605
606 let auth_key = {
607 let mut buffer = [0; 256];
608 let gab_bytes = gab.to_bytes_be();
609 let skip = buffer.len() - gab_bytes.len(); buffer[skip..].copy_from_slice(&gab_bytes);
611 AuthKey::from_bytes(buffer)
612 };
613
614 let new_nonce_hash = auth_key.calc_new_nonce_hash(&new_nonce, dh_gen.nonce_number);
615 check_new_nonce_hash(&dh_gen.new_nonce_hash, &new_nonce_hash)?;
616
617 let first_salt = {
618 let mut buffer = [0; 8];
619 buffer
620 .iter_mut()
621 .zip(&new_nonce[..8])
622 .zip(&server_nonce[..8])
623 .for_each(|((x, a), b)| *x = a ^ b);
624 i64::from_le_bytes(buffer)
625 };
626
627 if TRACE_AUTH_GEN {
628 println!("a {}", hex::to_hex(&auth_key.to_bytes()));
629 println!("o {time_offset}");
630 println!("s {first_salt}");
631 }
632
633 if dh_gen.nonce_number == 1 {
635 Ok(Finished {
636 auth_key: auth_key.to_bytes(),
637 time_offset,
638 first_salt,
639 })
640 } else {
641 Err(Error::DhGenFail)
642 }
643}
644
645fn check_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
647 if got == expected {
648 Ok(())
649 } else {
650 Err(Error::InvalidNonce {
651 got: *got,
652 expected: *expected,
653 })
654 }
655}
656
657fn check_server_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
660 if got == expected {
661 Ok(())
662 } else {
663 Err(Error::InvalidServerNonce {
664 got: *got,
665 expected: *expected,
666 })
667 }
668}
669
670fn check_new_nonce_hash(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
673 if got == expected {
674 Ok(())
675 } else {
676 Err(Error::InvalidNewNonceHash {
677 got: *got,
678 expected: *expected,
679 })
680 }
681}
682
683fn check_g_in_range(value: &BigUint, low: &BigUint, high: &BigUint) -> Result<(), Error> {
686 if low < value && value < high {
687 Ok(())
688 } else {
689 Err(Error::GParameterOutOfRange {
690 value: value.clone(),
691 low: low.clone(),
692 high: high.clone(),
693 })
694 }
695}
696
697#[allow(clippy::unreadable_literal)]
699fn key_for_fingerprint(fingerprint: i64) -> Option<rsa::Key> {
700 Some(match fingerprint {
701 -3414540481677951611 => rsa::Key::new("29379598170669337022986177149456128565388431120058863768162556424047512191330847455146576344487764408661701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531316010870401287642763009136156734339538042419388722777357134487746169093539093850251243897188928735903389451772730245253062963384108812842079887538976360465290946139638691491496062099570836476454855996319192747663615955633778034897140982517446405334423701359108810182097749467210509584293428076654573384828809574217079944388301239431309115013843331317877374435868468779972014486325557807783825502498215169806323", "65537").unwrap(),
703 -5595554452916591101 => rsa::Key::new("25342889448840415564971689590713473206898847759084779052582026594546022463853940585885215951168491965708222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166848940585952337613333022396096584117954892216031229237302943701877588456738335398602461675225081791820393153757504952636234951323237820036543581047826906120927972487366805292115792231423684261262330394324750785450942589751755390156647751460719351439969059949569615302809050721500330239005077889855323917509948255722081644689442127297605422579707142646660768825302832201908302295573257427896031830742328565032949", "65537").unwrap(),
705
706 _ => return None
707 })
708}
709
710#[cfg(test)]
711mod tests {
712 use super::*;
713
714 #[test]
715 fn emulate_successful_auth_key_gen_flow() -> Result<(), Error> {
716 let step1_random = hex::from_hex("4e44b426241e8b839153122d44585ac6")
717 .as_slice()
718 .try_into()
719 .unwrap();
720 let step1_request = hex::from_hex("f18e7ebe4e44b426241e8b839153122d44585ac6");
721 let step1_response = hex::from_hex(
722 "632416054e44b426241e8b839153122d44585ac665ba0b393e1094329eda2c42d62833030819546f942a11278d00000015c4b51c0300000003268d20df9858b2029f4ba16d109296216be86c022bb4c3",
723 );
724 let step2_random = hex::from_hex("b9dce68b05ef760fa7edfefeff45aaa8afbac11dc3d333bc3132fd16ab816d63ed93c5bef9d0452add8164a2d5df5804277ee5a06fd4523372707ddbd8106d03766d76fb8bec672bdcddcd225f7766b83663b32a0fda1055175c5582edd10430937666be4fd15510ba5f19aa645973b6e4e9270efac25b58741635fe84dd0af07a4686f750bf34de1073f1e7fa24e9b01a76e537504bd52b8195e5b78c9af2baa982454e1a99eeae0f35944089ad12726d2433a2c18c9698a725364f9c4e939ce4f1aee3891e58b85de90c88cc2eaef5db1841a594c0edc13cb4b7480a7e564fe892f82282d03ed07eb5ceac6644247bb137241166fe194756dfcffd68c6c345").as_slice().try_into().unwrap();
725 let step2_request = hex::from_hex(
726 "bee412d74e44b426241e8b839153122d44585ac665ba0b393e1094329eda2c42d62833030444b2e50d000000045e63ac8100000003268d20df9858b2fe0001007ec37ca8a84aa1b26d21bc8ac28b261ffa57b44e29f0d6722261e9b436059cc80ae9768a3ae4fbefe46cfbb76b88a1f80a1ebd95ae5d17bf655ed1015755e04c483a01cf4094a0830864054a71a0ac8a5ec34d6b24a69bf66c9654b32a8c65b0302718351b28f72a9a49610d5259b6edb6da37acc5fedc47d1a09c58df2c7eccbfaf54dfe123ebc253d9069f74e8be128051e5d280b3c9a5e8d3c6da344cb7374a6d410d4e088cc0eda3d8b1108ba4f4a85d79fbd2758000723780bc5459f59fd1cea1b511b77cc1411781d3feb57b14a97726cf3d2146cf43e648a69ff9cb5d48a31f543bd5bc3a023cf382d86d36bbfbbcb5e4a136acee25fd8e3e597e714d",
727 );
728 let step2_response = hex::from_hex(
729 "5c07e8d04e44b426241e8b839153122d44585ac665ba0b393e1094329eda2c42d6283303fe500200fd064e91012ade621b26a48ac7dc8b2c8670ed67092a00fe8c936483e4b02822c3cc655aaffe00542e311df5abdaa645b1da85ca50a6c7b0e7cc7cb2b23d42c84e288bb3b5cfe313e1ebafe19833916df4d1f58dba62e0ac49cac17a31b8b0d57d43eefda546d67e80e311c4b213adec9635c73f75a18ffb26fb71391523bd5ddfcc8be51b36d6b2552394c511ec935d53811a981baca62a2b58cbfe96f1b35e118e5e17456994aea931839925c4578f281f3f129d28026ec80224617a9ca8c615a12fba9c53e774476567f07b01a59d2e6635e39c16dc0a54679f3b54b0482f1cbeac821147d93d7365f4e23fb5794eb5fd4ffdc6456638ea32f641f49ee705e7b0da71cb75753e2f4f80d5af07edb017948f332e34a9c5886b0c86281e0e7228d5a652a9faaf819f7686c099186169aaa377c136fac57b69b7f7b383aaece652f8dcb14e0dfb23e2a65330307a74c31c508cc504450fa208eee14d8bbead1c1f90ccfc183ae1d3345c62424ea3477776204e8fe69efbb6a27b168913d3babaca30aa1c9589d6655b2ad4cd59f67e9b3957ab3270d70afab9bd488a6c5f39ca739ca8947def00cdb8812152731710f5108235775a019d3b4986d6b720b05167b4ee731a10a29fc1e03c42e99d8ff5cf64f45070c2f5ce485ea5fddc281728b6e4d0dea561c9097e3f8a54b055b0c069a9f8207520f6429eb5225c985e3379f2cf6754f56d414fcd00d502e69223b911b915978e0890a9ef128715b828bf3fda3fee6c7b9b2621d971a6f7820f89f4c4c2ab29dec00007c3ec6cead64f7f5802d5e6a4a16a185cfbfced5351fa68380e",
730 );
731 let step3_random = hex::from_hex("8fc3605a4604cbb5461fdeff439c761150083cdd502550558e92c730d46c9caf0b1b2d64d2c264942c50d98694fff604fdd2bd87f2cafb719bc55e65a1f60b08809660a650721c40d56fc9c792df1d463aad1718c6924b7bdffbe395f14633d33fc38ce47c18a1561b83a5c66d29f9e292637127471c3baab0028ae42796b689e53a7f9ab5f0ee6d3fb658d847c1abca509fc4ed0d45edbb1c946488910d8d78fa0767255b57a7c3898da8d26625bde40c5a0e80b581408ecd95a17d396dc7574a8ed3cbc4c085197ffaad29c18e577eb292aa8b98caa92efd6f9536049b5a7defc861e270eca90c55b9585405cb96f3e6ea754850b09e7a59ba5fd92d357982915d39752aaa2ec16b6cbde6a6c33971").as_slice().try_into().unwrap();
732 let step3_request = hex::from_hex(
733 "1f5f04f54e44b426241e8b839153122d44585ac665ba0b393e1094329eda2c42d6283303fe500100def448d48c608480bab65df3f8990be8011f7b415a6f8113617bea749b8b0ea6a937987b18cc4dcce8197efdcf8d6ec6af7fc3364b4945df77e4a1ae9db7acea4abcd73247edb36bde20fc969c1d55717277afe0bc31a9ee99f7d822f91fa2dc69c868a19511b162d55e0814d0292b7708b67d57eb04569349d5a20ffe85c0141fc17e9bbbaf207bef56e66decda718c52c45273f868c2eff89bb06355cd515fbfe123d719b244234867d2889c9d0e4436ba644076e5014a78af60b2f0e1b30285f4f71539bcf8c506ccafd62cfcd1b040fe5e35bb30e519ad56d753100f604e3ea5d02409d74dd3ab0861227410f1e13591cf2a638347e6c6d0bcae14e0e8753313b51daee40a67407b5cc8b213856a290a0c7b6cda9ff9c58d69faaf6a748cff05512b69f1380f7a36843edecdc764048bc16d9808f353a9caf6d49ca8b717c8f6de037518a444931a7da2b80f16d0",
734 );
735 let step3_response = hex::from_hex(
736 "34f7cb3b4e44b426241e8b839153122d44585ac665ba0b393e1094329eda2c42d628330313b781a0de4ab6bc7ab414cbe13f9f86",
737 );
738 let expected_auth_key = hex::from_hex("7582e48ad36cd6eef7944ac9bd7027de9ee3202543b68850ac01e1221350f7174e6c3771c9d86b3075f777539c23d053e9da9a1510d49e8fa0ad76a016ce28bfe3543dde69959bc682dab762b95a36629a8438e65baa53cc79b551c23d555c7675a36f4ece90882ece497d28a903409b780a8a80516cb0f8534fee3a67530beb2b1929626e07c2a052c4870b18b0a626606ca05cb13668a65aee3fa32cbebf1b3a56532138cb22c017cac44a292021902eea9b9f906c6be19c9203c7bb3ebc5f1b2044d0a90cb008f7248c3ae4449e0895b6090abb04c24131c2948bd27d879ecb934e50a46671f987653385ab388e4fa1ddd4c95743111e08bf11fef1f8f739").as_slice().try_into().unwrap();
739 let expected_time_offset = 0;
740 let expected_first_salt = 4459407212920268508;
741
742 let (request, data) = do_step1(&step1_random)?;
743 assert_eq!(request.to_bytes(), step1_request);
744 let response = tl::enums::ResPq::from_bytes(&step1_response).unwrap();
745
746 let (request, data) = do_step2(data, response, &step2_random)?;
747 assert_eq!(request.to_bytes(), step2_request);
748 let response = tl::enums::ServerDhParams::from_bytes(&step2_response).unwrap();
749
750 let step3_now = 1693436740;
751 let (request, data) = do_step3(data, response, &step3_random, step3_now)?;
752 assert_eq!(request.to_bytes(), step3_request);
753 let response = tl::enums::SetClientDhParamsAnswer::from_bytes(&step3_response).unwrap();
754
755 let finished = create_key(data, response)?;
756 assert_eq!(
757 finished,
758 Finished {
759 auth_key: expected_auth_key,
760 time_offset: expected_time_offset,
761 first_salt: expected_first_salt,
762 }
763 );
764
765 Ok(())
766 }
767}