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//! ```
36use 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
44// NOTE! Turning this on will leak the key generation process to stdout!
45// Should only be used for debugging purposes and generating test cases.
46const TRACE_AUTH_GEN: bool = false;
47
48/// The error type for Authorization Key generation process.
49#[derive(Clone, Debug, PartialEq)]
50pub enum Error {
51    /// The server's nonce did not match ours.
52    InvalidNonce {
53        /// The unexpected nonce that we got.
54        got: [u8; 16],
55
56        /// The expected nonce.
57        expected: [u8; 16],
58    },
59
60    /// The server's PQ number was not of the right size.
61    InvalidPqSize {
62        /// The unexpected size that we got.
63        size: usize,
64    },
65
66    /// None of the server fingerprints are known to us.
67    UnknownFingerprints {
68        /// The list of fingerprint that we got.
69        fingerprints: Vec<i64>,
70    },
71
72    /// The server failed to send the Diffie-Hellman parameters.
73    DhParamsFail,
74
75    /// The server's nonce has changed during the key exchange.
76    InvalidServerNonce {
77        /// The unexpected nonce that we got.
78        got: [u8; 16],
79
80        /// The expected nonce.
81        expected: [u8; 16],
82    },
83
84    /// The server's `encrypted_data` is not correctly padded.
85    EncryptedResponseNotPadded {
86        /// The non-padded length of the response.
87        len: usize,
88    },
89
90    /// An error occured while trying to read the DH inner data.
91    InvalidDhInnerData {
92        /// The inner error that occured when reading the data.
93        error: tl::deserialize::Error,
94    },
95
96    /// Some parameter (`g`, `g_a` or `g_b`) was out of range.
97    GParameterOutOfRange {
98        value: BigUint,
99        low: BigUint,
100        high: BigUint,
101    },
102
103    // The generation of Diffie-Hellman parameters is to be retried.
104    DhGenRetry,
105
106    // The generation of Diffie-Hellman parameters failed.
107    DhGenFail,
108
109    /// The plaintext answer hash did not match.
110    InvalidAnswerHash {
111        /// The unexpected hash that we got.
112        got: [u8; 20],
113
114        /// The expected hash.
115        expected: [u8; 20],
116    },
117
118    // The new nonce hash did not match.
119    InvalidNewNonceHash {
120        /// The unexpected nonce that we got.
121        got: [u8; 16],
122
123        /// The expected nonce.
124        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
169/// The data generated by [`step1`], needed for [`step2`].
170///
171/// [`step1`]: fn.step1.html
172/// [`step2`]: fn.step2.html
173pub struct Step1 {
174    nonce: [u8; 16],
175}
176
177/// The data generated by [`step2`], needed for [`step3`].
178///
179/// [`step2`]: fn.step2.html
180/// [`step3`]: fn.step3.html
181pub struct Step2 {
182    nonce: [u8; 16],
183    server_nonce: [u8; 16],
184    new_nonce: [u8; 32],
185}
186
187/// The data generated by [`step3`], needed for [`create_key`].
188///
189/// [`step3`]: fn.step3.html
190/// [`create_key`]: fn.create_key.html
191pub 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
199/// The first step of the process to generate an Authorization Key.
200pub 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
220// n.b.: the `do_step` functions are pure so that they can be tested.
221fn do_step1(random_bytes: &[u8; 16]) -> Result<(tl::functions::ReqPqMulti, Step1), Error> {
222    // Step 1. Generates a secure random nonce.
223    let nonce = *random_bytes;
224    Ok((tl::functions::ReqPqMulti { nonce }, Step1 { nonce }))
225}
226
227/// The second step of the process to generate an Authorization Key.
228pub 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    // Step 2. Validate the PQ response. Return `(p, q)` if it's valid.
261    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    // Remove the now-used first part from our available random data.
286    let random_bytes = {
287        let mut buffer = [0; 224];
288        buffer.copy_from_slice(&random_bytes[32..]);
289        buffer
290    };
291
292    // Convert (p, q) to bytes using the least amount of space possible.
293    // If we don't do this, Telegram will respond with -404 as the message.
294    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    // "pq is a representation of a natural number (in binary big endian format)"
310    // https://core.telegram.org/mtproto/auth_key#dh-exchange-initiation
311    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    // sha_digest + data + random_bytes
322    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    // Safe to unwrap because we found it just above
337    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
357/// The third step of the process to generate an Authorization Key.
358pub 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    // Step 3. Factorize PQ and construct the request for DH params.
404    let server_dh_params = match server_dh_params {
405        tl::enums::ServerDhParams::Fail(server_dh_params) => {
406            // Even though this is a failing case, we should still perform
407            // all the security checks.
408            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    // Complete DH Exchange
438    let (key, iv) = grammers_crypto::generate_key_data_from_nonce(&server_nonce, &new_nonce);
439
440    // sha1 hash + plain text + padding
441    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    // Use a cursor explicitly so we know where it ends (and most importantly
451    // where the padding starts).
452    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    // Safe to unwrap because the numbers are valid
475    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    // Remove the now-used first part from our available random data.
486    let random_bytes = {
487        let mut buffer = [0u8; 16];
488        buffer.copy_from_slice(&random_bytes[256..]);
489        buffer
490    };
491
492    // IMPORTANT: Apart from the conditions on the Diffie-Hellman prime
493    // dh_prime and generator g, both sides are to check that g, g_a and
494    // g_b are greater than 1 and less than dh_prime - 1. We recommend
495    // checking that g_a and g_b are between 2^{2048-64} and
496    // dh_prime - 2^{2048-64} as well.
497    // (https://core.telegram.org/mtproto/auth_key#dh-key-exchange-complete)
498    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    // Prepare client DH Inner Data
508    let client_dh_inner = tl::enums::ClientDhInnerData::Data(tl::types::ClientDhInnerData {
509        nonce,
510        server_nonce,
511        retry_id: 0, // TODO use an actual retry_id
512        g_b: g_b.to_bytes_be(),
513    })
514    .to_bytes();
515
516    // sha1(client_dh_inner).digest() + client_dh_inner
517    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        // Make sure we pad it ourselves, or else `encrypt_ige` will,
530        // introducing randomness.
531        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/// The final result of doing the authorization handshake, generated by [`create_key`].
556///
557/// [`create_key`]: fn.create_key.html
558#[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
565/// The last step of the process to generate an Authorization Key.
566pub 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(); // gab might need less than 256 bytes
618        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    // 1 for DhGenOk
642    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
653/// Helper function to avoid the boilerplate of checking for invalid nonce.
654fn 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
665/// Helper function to avoid the boilerplate of checking for invalid
666/// server nonce.
667fn 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
678/// Helper function to avoid the boilerplate of checking for invalid
679/// new nonce hash.
680fn 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
691/// Helper function to avoid the boilerplate of checking for `g` not being
692/// inside a valid range.
693fn 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/// Find the RSA key's `(n, e)` pair for a certain fingerprint.
706#[allow(clippy::unreadable_literal)]
707fn key_for_fingerprint(fingerprint: i64) -> Option<rsa::Key> {
708    Some(match fingerprint {
709        // Production
710        -3414540481677951611 => rsa::Key::new("29379598170669337022986177149456128565388431120058863768162556424047512191330847455146576344487764408661701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531316010870401287642763009136156734339538042419388722777357134487746169093539093850251243897188928735903389451772730245253062963384108812842079887538976360465290946139638691491496062099570836476454855996319192747663615955633778034897140982517446405334423701359108810182097749467210509584293428076654573384828809574217079944388301239431309115013843331317877374435868468779972014486325557807783825502498215169806323", "65537").unwrap(),
711        // Test
712        -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}