Skip to main content

layer_mtproto/
authentication.rs

1//! Sans-IO MTProto authorization key generation.
2//!
3//! # Flow
4//!
5//! ```text
6//! let (req, s1) = authentication::step1()?;
7//! // send req, receive resp (ResPQ)
8//! let (req, s2) = authentication::step2(s1, resp, dc_id)?;
9//! // send req, receive resp (ServerDhParams)
10//! let (req, s3) = authentication::step3(s2, resp)?;
11//! // send req, receive resp (SetClientDhParamsAnswer)
12//! let result = authentication::finish(s3, resp)?;
13//! // on FinishResult::Done(d): d.auth_key is ready
14//! // on FinishResult::Retry{..}: call retry_step3() + finish() up to 5 times
15//! ```
16
17use std::fmt;
18use std::time::{SystemTime, UNIX_EPOCH};
19
20use layer_crypto::{AuthKey, aes, check_p_and_g, factorize, generate_key_data_from_nonce, rsa};
21use layer_tl_types::{Cursor, Deserializable, Serializable};
22use num_bigint::BigUint;
23use sha1::{Digest, Sha1};
24
25// ---------------------------------------------------------------------------
26// Manual TL serialization helper for PQInnerDataDc
27//
28// Constructor: p_q_inner_data_dc#a9f55f95
29//   pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int
30//
31// TL "string" (bytes) encoding: if len < 254 → [len_byte, data..., 0-pad to 4-align],
32// else [0xfe, len_lo, len_mid, len_hi, data..., 0-pad to 4-align].
33// ---------------------------------------------------------------------------
34fn tl_serialize_bytes(v: &[u8]) -> Vec<u8> {
35    let len = v.len();
36    let mut out = Vec::new();
37    if len < 254 {
38        out.push(len as u8);
39        out.extend_from_slice(v);
40        let total = 1 + len;
41        let pad = (4 - total % 4) % 4;
42        out.extend(std::iter::repeat(0u8).take(pad));
43    } else {
44        out.push(0xfe);
45        out.push((len & 0xff) as u8);
46        out.push(((len >> 8) & 0xff) as u8);
47        out.push(((len >> 16) & 0xff) as u8);
48        out.extend_from_slice(v);
49        let total = 4 + len;
50        let pad = (4 - total % 4) % 4;
51        out.extend(std::iter::repeat(0u8).take(pad));
52    }
53    out
54}
55
56/// Serialize a `p_q_inner_data_dc` (constructor 0xa9f55f95) from raw fields.
57/// This is needed because the generated TL bindings only expose `PQInnerData`
58/// (legacy, no DC id) which Telegram rejects for non-DC2 connections.
59fn serialize_pq_inner_data_dc(
60    pq: &[u8],
61    p: &[u8],
62    q: &[u8],
63    nonce: &[u8; 16],
64    server_nonce: &[u8; 16],
65    new_nonce: &[u8; 32],
66    dc_id: i32,
67) -> Vec<u8> {
68    let mut out = Vec::new();
69    // Constructor id (little-endian)
70    out.extend_from_slice(&0xa9f55f95_u32.to_le_bytes());
71    out.extend(tl_serialize_bytes(pq));
72    out.extend(tl_serialize_bytes(p));
73    out.extend(tl_serialize_bytes(q));
74    out.extend_from_slice(nonce);
75    out.extend_from_slice(server_nonce);
76    out.extend_from_slice(new_nonce);
77    out.extend_from_slice(&dc_id.to_le_bytes());
78    out
79}
80
81// Error
82
83/// Errors that can occur during auth key generation.
84#[allow(missing_docs)]
85#[derive(Clone, Debug, PartialEq)]
86pub enum Error {
87    InvalidNonce {
88        got: [u8; 16],
89        expected: [u8; 16],
90    },
91    InvalidPqSize {
92        size: usize,
93    },
94    UnknownFingerprints {
95        fingerprints: Vec<i64>,
96    },
97    DhParamsFail,
98    InvalidServerNonce {
99        got: [u8; 16],
100        expected: [u8; 16],
101    },
102    EncryptedResponseNotPadded {
103        len: usize,
104    },
105    InvalidDhInnerData {
106        error: layer_tl_types::deserialize::Error,
107    },
108    InvalidDhPrime {
109        source: layer_crypto::DhError,
110    },
111    GParameterOutOfRange {
112        value: BigUint,
113        low: BigUint,
114        high: BigUint,
115    },
116    DhGenRetry,
117    DhGenFail,
118    InvalidAnswerHash {
119        got: [u8; 20],
120        expected: [u8; 20],
121    },
122    InvalidNewNonceHash {
123        got: [u8; 16],
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, "nonce mismatch: got {got:?}, expected {expected:?}")
135            }
136            Self::InvalidPqSize { size } => write!(f, "pq size {size} invalid (expected 8)"),
137            Self::UnknownFingerprints { fingerprints } => {
138                write!(f, "no known fingerprint in {fingerprints:?}")
139            }
140            Self::DhParamsFail => write!(f, "server returned DH params failure"),
141            Self::InvalidServerNonce { got, expected } => write!(
142                f,
143                "server_nonce mismatch: got {got:?}, expected {expected:?}"
144            ),
145            Self::EncryptedResponseNotPadded { len } => {
146                write!(f, "encrypted answer len {len} is not 16-byte aligned")
147            }
148            Self::InvalidDhInnerData { error } => {
149                write!(f, "DH inner data deserialization error: {error}")
150            }
151            Self::InvalidDhPrime { source } => {
152                write!(f, "DH prime/generator validation failed: {source}")
153            }
154            Self::GParameterOutOfRange { value, low, high } => {
155                write!(f, "g={value} not in range ({low}, {high})")
156            }
157            Self::DhGenRetry => write!(f, "DH gen retry requested"),
158            Self::DhGenFail => write!(f, "DH gen failed"),
159            Self::InvalidAnswerHash { got, expected } => write!(
160                f,
161                "answer hash mismatch: got {got:?}, expected {expected:?}"
162            ),
163            Self::InvalidNewNonceHash { got, expected } => write!(
164                f,
165                "new nonce hash mismatch: got {got:?}, expected {expected:?}"
166            ),
167        }
168    }
169}
170
171// Step state
172
173/// State after step 1.
174pub struct Step1 {
175    nonce: [u8; 16],
176}
177
178/// State after step 2.
179#[derive(Clone)]
180pub struct Step2 {
181    nonce: [u8; 16],
182    server_nonce: [u8; 16],
183    new_nonce: [u8; 32],
184}
185
186/// Pre-processed server DH parameters retained so that step 3 can be
187/// repeated on `dh_gen_retry` without having to re-decrypt the server response.
188#[derive(Clone)]
189pub struct DhParamsForRetry {
190    /// Server-supplied DH prime (big-endian bytes).
191    pub dh_prime: Vec<u8>,
192    /// DH generator `g`.
193    pub g: u32,
194    /// Server's public DH value `g_a` (big-endian bytes).
195    pub g_a: Vec<u8>,
196    /// Server's reported Unix timestamp (used to compute `time_offset`).
197    pub server_time: i32,
198    /// AES key derived from nonces for this session's IGE encryption.
199    pub aes_key: [u8; 32],
200    /// AES IV derived from nonces for this session's IGE encryption.
201    pub aes_iv: [u8; 32],
202}
203
204/// State after step 3.
205pub struct Step3 {
206    nonce: [u8; 16],
207    server_nonce: [u8; 16],
208    new_nonce: [u8; 32],
209    time_offset: i32,
210    /// Auth key candidate bytes (needed to derive `auth_key_aux_hash` on retry).
211    auth_key: [u8; 256],
212    /// The processed DH parameters stored so `retry_step3` can re-derive g_b
213    /// without re-parsing the encrypted server response.
214    pub dh_params: DhParamsForRetry,
215}
216
217/// Result of [`finish`] either the handshake is done, or the server wants us
218/// to retry step 3 with the `auth_key_aux_hash` as `retry_id`.
219pub enum FinishResult {
220    /// Handshake complete.
221    Done(Finished),
222    /// Server sent `dh_gen_retry`.  Call [`retry_step3`] with the returned
223    /// `retry_id` and the stored [`DhParamsForRetry`] from the previous Step3.
224    Retry {
225        /// The `auth_key_aux_hash` to embed as `retry_id` in the next attempt.
226        retry_id: i64,
227        /// DH parameters to feed back into [`retry_step3`].
228        dh_params: DhParamsForRetry,
229        /// Client nonce from the original step 1.
230        nonce: [u8; 16],
231        /// Server nonce from the ResPQ response.
232        server_nonce: [u8; 16],
233        /// Fresh nonce generated in step 2.
234        new_nonce: [u8; 32],
235    },
236}
237
238/// The final output of a successful auth key handshake.
239#[derive(Clone, Debug, PartialEq)]
240pub struct Finished {
241    /// The 256-byte Telegram authorization key.
242    pub auth_key: [u8; 256],
243    /// Clock skew in seconds relative to the server.
244    pub time_offset: i32,
245    /// Initial server salt.
246    pub first_salt: i64,
247}
248
249// Step 1: req_pq_multi
250
251/// Generate a `req_pq_multi` request. Returns the request + opaque state.
252pub fn step1() -> Result<(layer_tl_types::functions::ReqPqMulti, Step1), Error> {
253    let mut buf = [0u8; 16];
254    getrandom::getrandom(&mut buf).expect("getrandom");
255    do_step1(&buf)
256}
257
258fn do_step1(random: &[u8; 16]) -> Result<(layer_tl_types::functions::ReqPqMulti, Step1), Error> {
259    let nonce = *random;
260    Ok((
261        layer_tl_types::functions::ReqPqMulti { nonce },
262        Step1 { nonce },
263    ))
264}
265
266// Step 2: req_DH_params
267
268/// Process `ResPQ` and generate `req_DH_params`.
269///
270/// `dc_id` must be the numerical DC id of the server we are connecting to
271/// (e.g. 1 … 5).  It is embedded in the `PQInnerDataDc` payload so that
272/// Telegram can reject misrouted handshakes on non-DC2 endpoints.
273pub fn step2(
274    data: Step1,
275    response: layer_tl_types::enums::ResPq,
276    dc_id: i32,
277) -> Result<(layer_tl_types::functions::ReqDhParams, Step2), Error> {
278    let mut rnd = [0u8; 256];
279    getrandom::getrandom(&mut rnd).expect("getrandom");
280    do_step2(data, response, &rnd, dc_id)
281}
282
283fn do_step2(
284    data: Step1,
285    response: layer_tl_types::enums::ResPq,
286    random: &[u8; 256],
287    dc_id: i32,
288) -> Result<(layer_tl_types::functions::ReqDhParams, Step2), Error> {
289    let Step1 { nonce } = data;
290
291    // ResPq has a single constructor: resPQ → variant ResPq
292    let layer_tl_types::enums::ResPq::ResPq(res_pq) = response;
293
294    check_nonce(&res_pq.nonce, &nonce)?;
295
296    if res_pq.pq.len() != 8 {
297        return Err(Error::InvalidPqSize {
298            size: res_pq.pq.len(),
299        });
300    }
301
302    let pq = u64::from_be_bytes(res_pq.pq.as_slice().try_into().unwrap());
303    let (p, q) = factorize(pq);
304
305    let mut new_nonce = [0u8; 32];
306    new_nonce.copy_from_slice(&random[..32]);
307
308    // random[32..256] is 224 bytes for RSA padding
309    let rnd224: &[u8; 224] = random[32..].try_into().unwrap();
310
311    fn trim_be(v: u64) -> Vec<u8> {
312        let b = v.to_be_bytes();
313        let skip = b.iter().position(|&x| x != 0).unwrap_or(7);
314        b[skip..].to_vec()
315    }
316
317    let p_bytes = trim_be(p);
318    let q_bytes = trim_be(q);
319
320    // Serialize PQInnerDataDc (constructor 0xa9f55f95) manually.
321    // The legacy PQInnerData constructor (#83c95aec, no dc field) is rejected
322    // by Telegram servers for connections to non-DC2 endpoints.
323    let pq_inner = serialize_pq_inner_data_dc(
324        &pq.to_be_bytes(),
325        &p_bytes,
326        &q_bytes,
327        &nonce,
328        &res_pq.server_nonce,
329        &new_nonce,
330        dc_id,
331    );
332
333    let fingerprint = res_pq
334        .server_public_key_fingerprints
335        .iter()
336        .copied()
337        .find(|&fp| key_for_fingerprint(fp).is_some())
338        .ok_or_else(|| Error::UnknownFingerprints {
339            fingerprints: res_pq.server_public_key_fingerprints.clone(),
340        })?;
341
342    let key = key_for_fingerprint(fingerprint).unwrap();
343    let ciphertext = rsa::encrypt_hashed(&pq_inner, &key, rnd224);
344
345    Ok((
346        layer_tl_types::functions::ReqDhParams {
347            nonce,
348            server_nonce: res_pq.server_nonce,
349            p: p_bytes,
350            q: q_bytes,
351            public_key_fingerprint: fingerprint,
352            encrypted_data: ciphertext,
353        },
354        Step2 {
355            nonce,
356            server_nonce: res_pq.server_nonce,
357            new_nonce,
358        },
359    ))
360}
361
362// Step 3: set_client_DH_params
363
364/// Process `ServerDhParams` into a reusable [`DhParamsForRetry`] + send the
365/// first `set_client_DH_params` request.
366///
367/// `retry_id` should be 0 on the first call, or `auth_key_aux_hash` (returned
368/// by [`finish`] as [`FinishResult::Retry`]) on subsequent attempts.
369pub fn step3(
370    data: Step2,
371    response: layer_tl_types::enums::ServerDhParams,
372) -> Result<(layer_tl_types::functions::SetClientDhParams, Step3), Error> {
373    let mut rnd = [0u8; 272];
374    getrandom::getrandom(&mut rnd).expect("getrandom");
375    let now = SystemTime::now()
376        .duration_since(UNIX_EPOCH)
377        .unwrap()
378        .as_secs() as i32;
379    do_step3(data, response, &rnd, now, 0)
380}
381
382/// Re-run the client DH params generation after a `dh_gen_retry` response.
383/// Feed the `dh_params`, `nonce`, `server_nonce`, `new_nonce` from
384/// [`FinishResult::Retry`] and the `retry_id` (= `auth_key_aux_hash`).
385pub fn retry_step3(
386    dh_params: &DhParamsForRetry,
387    nonce: [u8; 16],
388    server_nonce: [u8; 16],
389    new_nonce: [u8; 32],
390    retry_id: i64,
391) -> Result<(layer_tl_types::functions::SetClientDhParams, Step3), Error> {
392    let mut rnd = [0u8; 272];
393    getrandom::getrandom(&mut rnd).expect("getrandom");
394    let now = SystemTime::now()
395        .duration_since(UNIX_EPOCH)
396        .unwrap()
397        .as_secs() as i32;
398    generate_client_dh_params(
399        dh_params,
400        nonce,
401        server_nonce,
402        new_nonce,
403        retry_id,
404        &rnd,
405        now,
406    )
407}
408
409fn generate_client_dh_params(
410    dh: &DhParamsForRetry,
411    nonce: [u8; 16],
412    server_nonce: [u8; 16],
413    new_nonce: [u8; 32],
414    retry_id: i64,
415    random: &[u8; 272],
416    now: i32,
417) -> Result<(layer_tl_types::functions::SetClientDhParams, Step3), Error> {
418    let dh_prime = BigUint::from_bytes_be(&dh.dh_prime);
419    let g = BigUint::from(dh.g);
420    let g_a = BigUint::from_bytes_be(&dh.g_a);
421    let time_offset = dh.server_time - now;
422
423    let b = BigUint::from_bytes_be(&random[..256]);
424    let g_b = g.modpow(&b, &dh_prime);
425
426    let one = BigUint::from(1u32);
427    let safety = one.clone() << (2048 - 64);
428    check_g_in_range(&g_b, &one, &(&dh_prime - &one))?;
429    check_g_in_range(&g_b, &safety, &(&dh_prime - &safety))?;
430
431    let client_dh_inner = layer_tl_types::enums::ClientDhInnerData::ClientDhInnerData(
432        layer_tl_types::types::ClientDhInnerData {
433            nonce,
434            server_nonce,
435            retry_id,
436            g_b: g_b.to_bytes_be(),
437        },
438    )
439    .to_bytes();
440
441    let digest: [u8; 20] = {
442        let mut sha = Sha1::new();
443        sha.update(&client_dh_inner);
444        sha.finalize().into()
445    };
446
447    let pad_len = (16 - ((20 + client_dh_inner.len()) % 16)) % 16;
448    let rnd16 = &random[256..256 + pad_len.min(16)];
449
450    let mut hashed = Vec::with_capacity(20 + client_dh_inner.len() + pad_len);
451    hashed.extend_from_slice(&digest);
452    hashed.extend_from_slice(&client_dh_inner);
453    hashed.extend_from_slice(&rnd16[..pad_len]);
454
455    let key: [u8; 32] = dh.aes_key;
456    let iv: [u8; 32] = dh.aes_iv;
457    aes::ige_encrypt(&mut hashed, &key, &iv);
458
459    // Compute auth_key = g_a^b mod dh_prime for this attempt.
460    let mut auth_key_bytes = [0u8; 256];
461    let gab_bytes = g_a.modpow(&b, &dh_prime).to_bytes_be();
462    let skip = 256 - gab_bytes.len();
463    auth_key_bytes[skip..].copy_from_slice(&gab_bytes);
464
465    Ok((
466        layer_tl_types::functions::SetClientDhParams {
467            nonce,
468            server_nonce,
469            encrypted_data: hashed,
470        },
471        Step3 {
472            nonce,
473            server_nonce,
474            new_nonce,
475            time_offset,
476            auth_key: auth_key_bytes,
477            dh_params: dh.clone(),
478        },
479    ))
480}
481
482fn do_step3(
483    data: Step2,
484    response: layer_tl_types::enums::ServerDhParams,
485    random: &[u8; 272],
486    now: i32,
487    retry_id: i64,
488) -> Result<(layer_tl_types::functions::SetClientDhParams, Step3), Error> {
489    let Step2 {
490        nonce,
491        server_nonce,
492        new_nonce,
493    } = data;
494
495    let mut server_dh_ok = match response {
496        layer_tl_types::enums::ServerDhParams::Fail(f) => {
497            check_nonce(&f.nonce, &nonce)?;
498            check_server_nonce(&f.server_nonce, &server_nonce)?;
499            return Err(Error::DhParamsFail);
500        }
501        layer_tl_types::enums::ServerDhParams::Ok(x) => x,
502    };
503
504    check_nonce(&server_dh_ok.nonce, &nonce)?;
505    check_server_nonce(&server_dh_ok.server_nonce, &server_nonce)?;
506
507    if server_dh_ok.encrypted_answer.len() % 16 != 0 {
508        return Err(Error::EncryptedResponseNotPadded {
509            len: server_dh_ok.encrypted_answer.len(),
510        });
511    }
512
513    let (key_arr, iv_arr) = generate_key_data_from_nonce(&server_nonce, &new_nonce);
514    aes::ige_decrypt(&mut server_dh_ok.encrypted_answer, &key_arr, &iv_arr);
515    let plain = server_dh_ok.encrypted_answer;
516
517    let got_hash: [u8; 20] = plain[..20].try_into().unwrap();
518    let mut cursor = Cursor::from_slice(&plain[20..]);
519
520    let inner = match layer_tl_types::enums::ServerDhInnerData::deserialize(&mut cursor) {
521        Ok(layer_tl_types::enums::ServerDhInnerData::ServerDhInnerData(x)) => x,
522        Err(e) => return Err(Error::InvalidDhInnerData { error: e }),
523    };
524
525    let expected_hash: [u8; 20] = {
526        let mut sha = Sha1::new();
527        sha.update(&plain[20..20 + cursor.pos()]);
528        sha.finalize().into()
529    };
530    if got_hash != expected_hash {
531        return Err(Error::InvalidAnswerHash {
532            got: got_hash,
533            expected: expected_hash,
534        });
535    }
536
537    check_nonce(&inner.nonce, &nonce)?;
538    check_server_nonce(&inner.server_nonce, &server_nonce)?;
539
540    check_p_and_g(&inner.dh_prime, inner.g as u32)
541        .map_err(|source| Error::InvalidDhPrime { source })?;
542
543    // Validate g_a range.
544    let dh_prime_bn = BigUint::from_bytes_be(&inner.dh_prime);
545    let one = BigUint::from(1u32);
546    let g_a_bn = BigUint::from_bytes_be(&inner.g_a);
547    let safety = one.clone() << (2048 - 64);
548    check_g_in_range(&g_a_bn, &safety, &(&dh_prime_bn - &safety))?;
549
550    let dh = DhParamsForRetry {
551        dh_prime: inner.dh_prime,
552        g: inner.g as u32,
553        g_a: inner.g_a,
554        server_time: inner.server_time,
555        aes_key: key_arr,
556        aes_iv: iv_arr,
557    };
558
559    generate_client_dh_params(&dh, nonce, server_nonce, new_nonce, retry_id, random, now)
560}
561
562// finish: create_key
563
564/// Finalise the handshake.
565///
566/// Returns [`FinishResult::Done`] on success or [`FinishResult::Retry`] when
567/// the server sends `dh_gen_retry` (up to 5 attempts are typical). On retry,
568/// call [`retry_step3`] with the returned fields, send the new request, receive
569/// the answer, then call `finish` again.
570pub fn finish(
571    data: Step3,
572    response: layer_tl_types::enums::SetClientDhParamsAnswer,
573) -> Result<FinishResult, Error> {
574    let Step3 {
575        nonce,
576        server_nonce,
577        new_nonce,
578        time_offset,
579        auth_key: auth_key_bytes,
580        dh_params,
581    } = data;
582
583    struct DhData {
584        nonce: [u8; 16],
585        server_nonce: [u8; 16],
586        hash: [u8; 16],
587        num: u8,
588    }
589
590    let dh = match response {
591        // Variant names come from the constructor names: dh_gen_ok → DhGenOk, etc.
592        layer_tl_types::enums::SetClientDhParamsAnswer::DhGenOk(x) => DhData {
593            nonce: x.nonce,
594            server_nonce: x.server_nonce,
595            hash: x.new_nonce_hash1,
596            num: 1,
597        },
598        layer_tl_types::enums::SetClientDhParamsAnswer::DhGenRetry(x) => DhData {
599            nonce: x.nonce,
600            server_nonce: x.server_nonce,
601            hash: x.new_nonce_hash2,
602            num: 2,
603        },
604        layer_tl_types::enums::SetClientDhParamsAnswer::DhGenFail(x) => DhData {
605            nonce: x.nonce,
606            server_nonce: x.server_nonce,
607            hash: x.new_nonce_hash3,
608            num: 3,
609        },
610    };
611
612    check_nonce(&dh.nonce, &nonce)?;
613    check_server_nonce(&dh.server_nonce, &server_nonce)?;
614
615    let auth_key = AuthKey::from_bytes(auth_key_bytes);
616    let expected_hash = auth_key.calc_new_nonce_hash(&new_nonce, dh.num);
617    check_new_nonce_hash(&dh.hash, &expected_hash)?;
618
619    let first_salt = {
620        let mut buf = [0u8; 8];
621        for ((dst, a), b) in buf.iter_mut().zip(&new_nonce[..8]).zip(&server_nonce[..8]) {
622            *dst = a ^ b;
623        }
624        i64::from_le_bytes(buf)
625    };
626
627    match dh.num {
628        1 => Ok(FinishResult::Done(Finished {
629            auth_key: auth_key.to_bytes(),
630            time_offset,
631            first_salt,
632        })),
633        2 => {
634            // dh_gen_retry: compute auth_key_aux_hash = SHA1(auth_key)[0..8] as i64 LE.
635            let aux_hash: [u8; 20] = {
636                let mut sha = Sha1::new();
637                sha.update(&auth_key.to_bytes());
638                sha.finalize().into()
639            };
640            let retry_id = i64::from_le_bytes(aux_hash[..8].try_into().unwrap());
641            Ok(FinishResult::Retry {
642                retry_id,
643                dh_params,
644                nonce,
645                server_nonce,
646                new_nonce,
647            })
648        }
649        _ => Err(Error::DhGenFail),
650    }
651}
652
653// Helpers
654
655fn check_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
656    if got == expected {
657        Ok(())
658    } else {
659        Err(Error::InvalidNonce {
660            got: *got,
661            expected: *expected,
662        })
663    }
664}
665fn check_server_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
666    if got == expected {
667        Ok(())
668    } else {
669        Err(Error::InvalidServerNonce {
670            got: *got,
671            expected: *expected,
672        })
673    }
674}
675fn check_new_nonce_hash(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
676    if got == expected {
677        Ok(())
678    } else {
679        Err(Error::InvalidNewNonceHash {
680            got: *got,
681            expected: *expected,
682        })
683    }
684}
685fn check_g_in_range(val: &BigUint, lo: &BigUint, hi: &BigUint) -> Result<(), Error> {
686    if lo < val && val < hi {
687        Ok(())
688    } else {
689        Err(Error::GParameterOutOfRange {
690            value: val.clone(),
691            low: lo.clone(),
692            high: hi.clone(),
693        })
694    }
695}
696
697/// RSA key by server fingerprint. Includes both production and test DC keys.
698#[allow(clippy::unreadable_literal)]
699pub fn key_for_fingerprint(fp: i64) -> Option<rsa::Key> {
700    Some(match fp {
701        // Production DC key (fingerprint -3414540481677951611)
702        -3414540481677951611 => rsa::Key::new(
703            "29379598170669337022986177149456128565388431120058863768162556424047512191330847455146576344487764408661701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531316010870401287642763009136156734339538042419388722777357134487746169093539093850251243897188928735903389451772730245253062963384108812842079887538976360465290946139638691491496062099570836476454855996319192747663615955633778034897140982517446405334423701359108810182097749467210509584293428076654573384828809574217079944388301239431309115013843331317877374435868468779972014486325557807783825502498215169806323",
704            "65537",
705        )?,
706        // Test DC key (fingerprint -5595554452916591101)
707        -5595554452916591101 => rsa::Key::new(
708            "25342889448840415564971689590713473206898847759084779052582026594546022463853940585885215951168491965708222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166848940585952337613333022396096584117954892216031229237302943701877588456738335398602461675225081791820393153757504952636234951323237820036543581047826906120927972487366805292115792231423684261262330394324750785450942589751755390156647751460719351439969059949569615302809050721500330239005077889855323917509948255722081644689442127297605422579707142646660768825302832201908302295573257427896031830742328565032949",
709            "65537",
710        )?,
711        _ => return None,
712    })
713}