Skip to main content

grammers_mtproto/
authentication.rs

1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Sans-IO implementation of the process to generate an Authorization Key.
10//!
11//! # Examples
12//!
13//! ```no_run
14//! use grammers_mtproto::authentication;
15//!
16//! fn send_data_to_server<R>(request: &R) -> Result<R::Return, authentication::Error>
17//! where R: grammers_tl_types::RemoteCall {
18//!     unimplemented!()
19//! }
20//!
21//! fn main() -> Result<(), authentication::Error> {
22//!     let (request, data) = authentication::step1()?;
23//!     let response = send_data_to_server(&request)?;
24//!
25//!     let (request, data) = authentication::step2(data, response)?;
26//!     let response = send_data_to_server(&request)?;
27//!
28//!     let (request, data) = authentication::step3(data, response)?;
29//!     let response = send_data_to_server(&request)?;
30//!
31//!     let authentication::Finished { auth_key, .. } = authentication::create_key(data, response)?;
32//!     // Now you have a secure `auth_key` to send encrypted messages to server.
33//!     Ok(())
34//! }
35//! ```
36
37use 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
46// NOTE! Turning this on will leak the key generation process to stdout!
47// Should only be used for debugging purposes and generating test cases.
48const TRACE_AUTH_GEN: bool = false;
49
50/// The error type for Authorization Key generation process.
51#[derive(Clone, Debug, PartialEq)]
52pub enum Error {
53    /// The server's nonce did not match ours.
54    InvalidNonce {
55        /// The unexpected nonce that we got.
56        got: [u8; 16],
57
58        /// The expected nonce.
59        expected: [u8; 16],
60    },
61
62    /// The server's PQ number was not of the right size.
63    InvalidPqSize {
64        /// The unexpected size that we got.
65        size: usize,
66    },
67
68    /// None of the server fingerprints are known to us.
69    UnknownFingerprints {
70        /// The list of fingerprint that we got.
71        fingerprints: Vec<i64>,
72    },
73
74    /// The server failed to send the Diffie-Hellman parameters.
75    DhParamsFail,
76
77    /// The server's nonce has changed during the key exchange.
78    InvalidServerNonce {
79        /// The unexpected nonce that we got.
80        got: [u8; 16],
81
82        /// The expected nonce.
83        expected: [u8; 16],
84    },
85
86    /// The server's `encrypted_data` is not correctly padded.
87    EncryptedResponseNotPadded {
88        /// The non-padded length of the response.
89        len: usize,
90    },
91
92    /// An error occured while trying to read the DH inner data.
93    InvalidDhInnerData {
94        /// The inner error that occured when reading the data.
95        error: tl::deserialize::Error,
96    },
97
98    /// Some parameter (`g`, `g_a` or `g_b`) was out of range.
99    GParameterOutOfRange {
100        value: BigUint,
101        low: BigUint,
102        high: BigUint,
103    },
104
105    // The generation of Diffie-Hellman parameters is to be retried.
106    DhGenRetry,
107
108    // The generation of Diffie-Hellman parameters failed.
109    DhGenFail,
110
111    /// The plaintext answer hash did not match.
112    InvalidAnswerHash {
113        /// The unexpected hash that we got.
114        got: [u8; 20],
115
116        /// The expected hash.
117        expected: [u8; 20],
118    },
119
120    // The new nonce hash did not match.
121    InvalidNewNonceHash {
122        /// The unexpected nonce that we got.
123        got: [u8; 16],
124
125        /// The expected nonce.
126        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
171/// The data generated by [`step1`], needed for [`step2`].
172pub struct Step1 {
173    nonce: [u8; 16],
174}
175
176/// The data generated by [`step2`], needed for [`step3`].
177pub struct Step2 {
178    nonce: [u8; 16],
179    server_nonce: [u8; 16],
180    new_nonce: [u8; 32],
181}
182
183/// The data generated by [`step3`], needed for [`create_key`].
184pub 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
192/// The first step of the process to generate an Authorization Key.
193pub 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
213// n.b.: the `do_step` functions are pure so that they can be tested.
214fn do_step1(random_bytes: &[u8; 16]) -> Result<(tl::functions::ReqPqMulti, Step1), Error> {
215    // Step 1. Generates a secure random nonce.
216    let nonce = *random_bytes;
217    Ok((tl::functions::ReqPqMulti { nonce }, Step1 { nonce }))
218}
219
220/// The second step of the process to generate an Authorization Key.
221pub 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    // Step 2. Validate the PQ response. Return `(p, q)` if it's valid.
254    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    // Remove the now-used first part from our available random data.
279    let random_bytes = {
280        let mut buffer = [0; 224];
281        buffer.copy_from_slice(&random_bytes[32..]);
282        buffer
283    };
284
285    // Convert (p, q) to bytes using the least amount of space possible.
286    // If we don't do this, Telegram will respond with -404 as the message.
287    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    // "pq is a representation of a natural number (in binary big endian format)"
303    // https://core.telegram.org/mtproto/auth_key#dh-exchange-initiation
304    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    // sha_digest + data + random_bytes
315    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    // Safe to unwrap because we found it just above
330    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
350/// The third step of the process to generate an Authorization Key.
351pub 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    // Step 3. Factorize PQ and construct the request for DH params.
397    let mut server_dh_params = match server_dh_params {
398        tl::enums::ServerDhParams::Fail(server_dh_params) => {
399            // Even though this is a failing case, we should still perform
400            // all the security checks.
401            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    // Complete DH Exchange
431    let (key, iv) = grammers_crypto::generate_key_data_from_nonce(&server_nonce, &new_nonce);
432
433    // sha1 hash + plain text + padding
434    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    // Use a cursor explicitly so we know where it ends (and most importantly
444    // where the padding starts).
445    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    // Safe to unwrap because the numbers are valid
468    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    // Remove the now-used first part from our available random data.
479    let random_bytes = {
480        let mut buffer = [0u8; 16];
481        buffer.copy_from_slice(&random_bytes[256..]);
482        buffer
483    };
484
485    // IMPORTANT: Apart from the conditions on the Diffie-Hellman prime
486    // dh_prime and generator g, both sides are to check that g, g_a and
487    // g_b are greater than 1 and less than dh_prime - 1. We recommend
488    // checking that g_a and g_b are between 2^{2048-64} and
489    // dh_prime - 2^{2048-64} as well.
490    // (https://core.telegram.org/mtproto/auth_key#dh-key-exchange-complete)
491    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    // Prepare client DH Inner Data
501    let client_dh_inner = tl::enums::ClientDhInnerData::Data(tl::types::ClientDhInnerData {
502        nonce,
503        server_nonce,
504        retry_id: 0, // TODO use an actual retry_id
505        g_b: g_b.to_bytes_be(),
506    })
507    .to_bytes();
508
509    // sha1(client_dh_inner).digest() + client_dh_inner
510    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        // Make sure we pad it ourselves, or else `encrypt_ige` will,
523        // introducing randomness.
524        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/// The final result of doing the authorization handshake, generated by [`create_key`].
550#[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
557/// The last step of the process to generate an Authorization Key.
558pub 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(); // gab might need less than 256 bytes
610        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    // 1 for DhGenOk
634    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
645/// Helper function to avoid the boilerplate of checking for invalid nonce.
646fn 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
657/// Helper function to avoid the boilerplate of checking for invalid
658/// server nonce.
659fn 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
670/// Helper function to avoid the boilerplate of checking for invalid
671/// new nonce hash.
672fn 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
683/// Helper function to avoid the boilerplate of checking for `g` not being
684/// inside a valid range.
685fn 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/// Find the RSA key's `(n, e)` pair for a certain fingerprint.
698#[allow(clippy::unreadable_literal)]
699fn key_for_fingerprint(fingerprint: i64) -> Option<rsa::Key> {
700    Some(match fingerprint {
701        // Production
702        -3414540481677951611 => rsa::Key::new("29379598170669337022986177149456128565388431120058863768162556424047512191330847455146576344487764408661701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531316010870401287642763009136156734339538042419388722777357134487746169093539093850251243897188928735903389451772730245253062963384108812842079887538976360465290946139638691491496062099570836476454855996319192747663615955633778034897140982517446405334423701359108810182097749467210509584293428076654573384828809574217079944388301239431309115013843331317877374435868468779972014486325557807783825502498215169806323", "65537").unwrap(),
703        // Test
704        -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}