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