1use std::fmt;
16use std::time::{SystemTime, UNIX_EPOCH};
17
18use ferogram_crypto::{
19 AuthKey, aes, check_p_and_g, factorize, fill_random, generate_key_data_from_nonce, rsa,
20};
21use ferogram_tl_types::{Cursor, Deserializable, Serializable};
22use num_bigint::BigUint;
23
24fn tl_serialize_bytes(v: &[u8]) -> Vec<u8> {
30 let len = v.len();
31 let mut out = Vec::new();
32 if len < 254 {
33 out.push(len as u8);
34 out.extend_from_slice(v);
35 let total = 1 + len;
36 let pad = (4 - total % 4) % 4;
37 out.extend(std::iter::repeat_n(0u8, pad));
38 } else {
39 out.push(0xfe);
40 out.push((len & 0xff) as u8);
41 out.push(((len >> 8) & 0xff) as u8);
42 out.push(((len >> 16) & 0xff) as u8);
43 out.extend_from_slice(v);
44 let total = 4 + len;
45 let pad = (4 - total % 4) % 4;
46 out.extend(std::iter::repeat_n(0u8, pad));
47 }
48 out
49}
50
51fn serialize_pq_inner_data(
54 pq: &[u8],
55 p: &[u8],
56 q: &[u8],
57 nonce: &[u8; 16],
58 server_nonce: &[u8; 16],
59 new_nonce: &[u8; 32],
60) -> Vec<u8> {
61 let mut out = Vec::new();
62 out.extend_from_slice(&0x83c95aec_u32.to_le_bytes());
63 out.extend(tl_serialize_bytes(pq));
64 out.extend(tl_serialize_bytes(p));
65 out.extend(tl_serialize_bytes(q));
66 out.extend_from_slice(nonce);
67 out.extend_from_slice(server_nonce);
68 out.extend_from_slice(new_nonce);
69 out
70}
71
72fn serialize_pq_inner_data_temp(
74 pq: &[u8],
75 p: &[u8],
76 q: &[u8],
77 nonce: &[u8; 16],
78 server_nonce: &[u8; 16],
79 new_nonce: &[u8; 32],
80 expires_in: i32,
81) -> Vec<u8> {
82 let mut out = Vec::new();
83 out.extend_from_slice(&0x3c6a84d4_u32.to_le_bytes());
84 out.extend(tl_serialize_bytes(pq));
85 out.extend(tl_serialize_bytes(p));
86 out.extend(tl_serialize_bytes(q));
87 out.extend_from_slice(nonce);
88 out.extend_from_slice(server_nonce);
89 out.extend_from_slice(new_nonce);
90 out.extend_from_slice(&expires_in.to_le_bytes());
91 out
92}
93
94fn serialize_pq_inner_data_dc(
99 pq: &[u8],
100 p: &[u8],
101 q: &[u8],
102 nonce: &[u8; 16],
103 server_nonce: &[u8; 16],
104 new_nonce: &[u8; 32],
105 dc_id: i32,
106) -> Vec<u8> {
107 let mut out = Vec::new();
108 out.extend_from_slice(&0xa9f55f95_u32.to_le_bytes());
110 out.extend(tl_serialize_bytes(pq));
111 out.extend(tl_serialize_bytes(p));
112 out.extend(tl_serialize_bytes(q));
113 out.extend_from_slice(nonce);
114 out.extend_from_slice(server_nonce);
115 out.extend_from_slice(new_nonce);
116 out.extend_from_slice(&dc_id.to_le_bytes());
117 out
118}
119
120#[allow(clippy::too_many_arguments)]
122fn serialize_pq_inner_data_temp_dc(
123 pq: &[u8],
124 p: &[u8],
125 q: &[u8],
126 nonce: &[u8; 16],
127 server_nonce: &[u8; 16],
128 new_nonce: &[u8; 32],
129 dc_id: i32,
130 expires_in: i32,
131) -> Vec<u8> {
132 let mut out = Vec::new();
133 out.extend_from_slice(&0x56fddf88_u32.to_le_bytes()); out.extend(tl_serialize_bytes(pq));
135 out.extend(tl_serialize_bytes(p));
136 out.extend(tl_serialize_bytes(q));
137 out.extend_from_slice(nonce);
138 out.extend_from_slice(server_nonce);
139 out.extend_from_slice(new_nonce);
140 out.extend_from_slice(&dc_id.to_le_bytes());
141 out.extend_from_slice(&expires_in.to_le_bytes()); out
143}
144
145#[allow(missing_docs)]
147#[derive(Clone, Debug, PartialEq)]
148pub enum Error {
149 InvalidNonce {
150 got: [u8; 16],
151 expected: [u8; 16],
152 },
153 InvalidPqSize {
154 size: usize,
155 },
156 FactorizationFailed {
157 pq: u64,
158 },
159 UnknownFingerprints {
160 fingerprints: Vec<i64>,
161 },
162 DhParamsFail,
163 InvalidServerNonce {
164 got: [u8; 16],
165 expected: [u8; 16],
166 },
167 EncryptedResponseNotPadded {
168 len: usize,
169 },
170 InvalidDhInnerData {
171 error: ferogram_tl_types::deserialize::Error,
172 },
173 InvalidDhPrime {
174 source: ferogram_crypto::DhError,
175 },
176 GParameterOutOfRange {
177 value: BigUint,
178 low: BigUint,
179 high: BigUint,
180 },
181 DhGenRetry,
182 DhGenFail,
183 InvalidAnswerHash {
184 got: [u8; 20],
185 expected: [u8; 20],
186 },
187 InvalidNewNonceHash {
188 got: [u8; 16],
189 expected: [u8; 16],
190 },
191}
192
193impl std::error::Error for Error {}
194
195impl fmt::Display for Error {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 match self {
198 Self::InvalidNonce { got, expected } => {
199 write!(f, "nonce mismatch: got {got:?}, expected {expected:?}")
200 }
201 Self::InvalidPqSize { size } => write!(f, "pq size {size} invalid (expected 8)"),
202 Self::FactorizationFailed { pq } => {
203 write!(f, "could not factorize server-supplied pq={pq}")
204 }
205 Self::UnknownFingerprints { fingerprints } => {
206 write!(f, "no known fingerprint in {fingerprints:?}")
207 }
208 Self::DhParamsFail => write!(f, "server returned DH params failure"),
209 Self::InvalidServerNonce { got, expected } => write!(
210 f,
211 "server_nonce mismatch: got {got:?}, expected {expected:?}"
212 ),
213 Self::EncryptedResponseNotPadded { len } => {
214 write!(f, "encrypted answer len {len} is not 16-byte aligned")
215 }
216 Self::InvalidDhInnerData { error } => {
217 write!(f, "DH inner data deserialization error: {error}")
218 }
219 Self::InvalidDhPrime { source } => {
220 write!(f, "DH prime/generator validation failed: {source}")
221 }
222 Self::GParameterOutOfRange { value, low, high } => {
223 write!(f, "g={value} not in range ({low}, {high})")
224 }
225 Self::DhGenRetry => write!(f, "DH gen retry requested"),
226 Self::DhGenFail => write!(f, "DH gen failed"),
227 Self::InvalidAnswerHash { got, expected } => write!(
228 f,
229 "answer hash mismatch: got {got:?}, expected {expected:?}"
230 ),
231 Self::InvalidNewNonceHash { got, expected } => write!(
232 f,
233 "new nonce hash mismatch: got {got:?}, expected {expected:?}"
234 ),
235 }
236 }
237}
238
239pub struct Step1 {
241 nonce: [u8; 16],
242}
243
244#[derive(Clone)]
246pub struct Step2 {
247 nonce: [u8; 16],
248 server_nonce: [u8; 16],
249 new_nonce: [u8; 32],
250}
251
252#[derive(Clone)]
255pub struct DhParamsForRetry {
256 pub dh_prime: Vec<u8>,
258 pub g: u32,
260 pub g_a: Vec<u8>,
262 pub server_time: i32,
264 pub aes_key: [u8; 32],
266 pub aes_iv: [u8; 32],
268}
269
270pub struct Step3 {
272 nonce: [u8; 16],
273 server_nonce: [u8; 16],
274 new_nonce: [u8; 32],
275 time_offset: i32,
276 auth_key: [u8; 256],
278 pub dh_params: DhParamsForRetry,
281}
282
283pub enum FinishResult {
286 Done(Finished),
288 Retry {
291 retry_id: i64,
293 dh_params: DhParamsForRetry,
295 nonce: [u8; 16],
297 server_nonce: [u8; 16],
299 new_nonce: [u8; 32],
301 },
302}
303
304#[derive(Clone, Debug, PartialEq)]
306pub struct Finished {
307 pub auth_key: [u8; 256],
309 pub time_offset: i32,
311 pub first_salt: i64,
313}
314
315pub fn step1() -> Result<(ferogram_tl_types::functions::ReqPqMulti, Step1), Error> {
317 let mut buf = [0u8; 16];
318 fill_random(&mut buf);
319 do_step1(&buf)
320}
321
322fn do_step1(random: &[u8; 16]) -> Result<(ferogram_tl_types::functions::ReqPqMulti, Step1), Error> {
323 let nonce = *random;
324 Ok((
325 ferogram_tl_types::functions::ReqPqMulti { nonce },
326 Step1 { nonce },
327 ))
328}
329
330pub fn step2(
335 data: Step1,
336 response: ferogram_tl_types::enums::ResPq,
337 dc_id: i32,
338) -> Result<(ferogram_tl_types::functions::ReqDhParams, Step2), Error> {
339 let mut rnd = [0u8; 256];
340 fill_random(&mut rnd);
341 do_step2_inner(data, response, &rnd, dc_id, None, false)
342}
343
344pub fn step2_temp(
348 data: Step1,
349 response: ferogram_tl_types::enums::ResPq,
350 dc_id: i32,
351 expires_in: i32,
352) -> Result<(ferogram_tl_types::functions::ReqDhParams, Step2), Error> {
353 let mut rnd = [0u8; 256];
354 fill_random(&mut rnd);
355 do_step2_inner(data, response, &rnd, dc_id, Some(expires_in), false)
356}
357
358pub fn step2_dc_tagged(
361 data: Step1,
362 response: ferogram_tl_types::enums::ResPq,
363 dc_id: i32,
364 expires_in: Option<i32>,
365) -> Result<(ferogram_tl_types::functions::ReqDhParams, Step2), Error> {
366 let mut rnd = [0u8; 256];
367 fill_random(&mut rnd);
368 do_step2_inner(data, response, &rnd, dc_id, expires_in, true)
369}
370
371fn do_step2_inner(
372 data: Step1,
373 response: ferogram_tl_types::enums::ResPq,
374 random: &[u8; 256],
375 dc_id: i32,
376 expires_in: Option<i32>,
377 tag_dc: bool,
378) -> Result<(ferogram_tl_types::functions::ReqDhParams, Step2), Error> {
379 let Step1 { nonce } = data;
380
381 let ferogram_tl_types::enums::ResPq::ResPq(res_pq) = response;
383
384 check_nonce(&res_pq.nonce, &nonce)?;
385
386 if res_pq.pq.len() != 8 {
387 return Err(Error::InvalidPqSize {
388 size: res_pq.pq.len(),
389 });
390 }
391
392 let pq = u64::from_be_bytes(res_pq.pq.as_slice().try_into().unwrap());
393 let (p, q) = factorize(pq).ok_or(Error::FactorizationFailed { pq })?;
394
395 let mut new_nonce = [0u8; 32];
396 new_nonce.copy_from_slice(&random[..32]);
397
398 let rnd224: &[u8; 224] = random[32..].try_into().unwrap();
400
401 fn trim_be(v: u64) -> Vec<u8> {
402 let b = v.to_be_bytes();
403 let skip = b.iter().position(|&x| x != 0).unwrap_or(7);
404 b[skip..].to_vec()
405 }
406
407 let p_bytes = trim_be(p);
408 let q_bytes = trim_be(q);
409
410 let pq_inner = match (tag_dc, expires_in) {
412 (true, Some(expires_in)) => serialize_pq_inner_data_temp_dc(
413 &pq.to_be_bytes(),
414 &p_bytes,
415 &q_bytes,
416 &nonce,
417 &res_pq.server_nonce,
418 &new_nonce,
419 dc_id,
420 expires_in,
421 ),
422 (true, None) => serialize_pq_inner_data_dc(
423 &pq.to_be_bytes(),
424 &p_bytes,
425 &q_bytes,
426 &nonce,
427 &res_pq.server_nonce,
428 &new_nonce,
429 dc_id,
430 ),
431 (false, Some(expires_in)) => serialize_pq_inner_data_temp(
432 &pq.to_be_bytes(),
433 &p_bytes,
434 &q_bytes,
435 &nonce,
436 &res_pq.server_nonce,
437 &new_nonce,
438 expires_in,
439 ),
440 (false, None) => serialize_pq_inner_data(
441 &pq.to_be_bytes(),
442 &p_bytes,
443 &q_bytes,
444 &nonce,
445 &res_pq.server_nonce,
446 &new_nonce,
447 ),
448 };
449
450 let fingerprint = res_pq
451 .server_public_key_fingerprints
452 .iter()
453 .copied()
454 .find(|&fp| key_for_fingerprint(fp).is_some())
455 .ok_or_else(|| Error::UnknownFingerprints {
456 fingerprints: res_pq.server_public_key_fingerprints.clone(),
457 })?;
458
459 let key = key_for_fingerprint(fingerprint).unwrap();
460 let ciphertext = rsa::encrypt_hashed(&pq_inner, &key, rnd224);
461
462 Ok((
463 ferogram_tl_types::functions::ReqDhParams {
464 nonce,
465 server_nonce: res_pq.server_nonce,
466 p: p_bytes,
467 q: q_bytes,
468 public_key_fingerprint: fingerprint,
469 encrypted_data: ciphertext,
470 },
471 Step2 {
472 nonce,
473 server_nonce: res_pq.server_nonce,
474 new_nonce,
475 },
476 ))
477}
478
479pub fn step3(
485 data: Step2,
486 response: ferogram_tl_types::enums::ServerDhParams,
487) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
488 let mut rnd = [0u8; 272];
489 fill_random(&mut rnd);
490 let now = SystemTime::now()
491 .duration_since(UNIX_EPOCH)
492 .unwrap()
493 .as_secs() as i32;
494 do_step3(data, response, &rnd, now, 0)
495}
496
497pub fn retry_step3(
501 dh_params: &DhParamsForRetry,
502 nonce: [u8; 16],
503 server_nonce: [u8; 16],
504 new_nonce: [u8; 32],
505 retry_id: i64,
506) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
507 let mut rnd = [0u8; 272];
508 fill_random(&mut rnd);
509 let now = SystemTime::now()
510 .duration_since(UNIX_EPOCH)
511 .unwrap()
512 .as_secs() as i32;
513 generate_client_dh_params(
514 dh_params,
515 nonce,
516 server_nonce,
517 new_nonce,
518 retry_id,
519 &rnd,
520 now,
521 )
522}
523
524fn generate_client_dh_params(
525 dh: &DhParamsForRetry,
526 nonce: [u8; 16],
527 server_nonce: [u8; 16],
528 new_nonce: [u8; 32],
529 retry_id: i64,
530 random: &[u8; 272],
531 now: i32,
532) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
533 let dh_prime = BigUint::from_bytes_be(&dh.dh_prime);
534 let g = BigUint::from(dh.g);
535 let g_a = BigUint::from_bytes_be(&dh.g_a);
536 let time_offset = dh.server_time - now;
537
538 let b = BigUint::from_bytes_be(&random[..256]);
539 let g_b = g.modpow(&b, &dh_prime);
540
541 let one = BigUint::from(1u32);
542 let safety = one.clone() << (2048 - 64);
543 check_g_in_range(&g_b, &one, &(&dh_prime - &one))?;
544 check_g_in_range(&g_b, &safety, &(&dh_prime - &safety))?;
545
546 let client_dh_inner = ferogram_tl_types::enums::ClientDhInnerData::ClientDhInnerData(
547 ferogram_tl_types::types::ClientDhInnerData {
548 nonce,
549 server_nonce,
550 retry_id,
551 g_b: g_b.to_bytes_be(),
552 },
553 )
554 .to_bytes();
555
556 let digest: [u8; 20] = ferogram_crypto::sha1!(&client_dh_inner);
557
558 let pad_len = (16 - ((20 + client_dh_inner.len()) % 16)) % 16;
559 let rnd16 = &random[256..256 + pad_len.min(16)];
560
561 let mut hashed = Vec::with_capacity(20 + client_dh_inner.len() + pad_len);
562 hashed.extend_from_slice(&digest);
563 hashed.extend_from_slice(&client_dh_inner);
564 hashed.extend_from_slice(&rnd16[..pad_len]);
565
566 let key: [u8; 32] = dh.aes_key;
567 let iv: [u8; 32] = dh.aes_iv;
568 aes::ige_encrypt(&mut hashed, &key, &iv);
569
570 let mut auth_key_bytes = [0u8; 256];
572 let gab_bytes = g_a.modpow(&b, &dh_prime).to_bytes_be();
573 let skip = 256 - gab_bytes.len();
574 auth_key_bytes[skip..].copy_from_slice(&gab_bytes);
575
576 Ok((
577 ferogram_tl_types::functions::SetClientDhParams {
578 nonce,
579 server_nonce,
580 encrypted_data: hashed,
581 },
582 Step3 {
583 nonce,
584 server_nonce,
585 new_nonce,
586 time_offset,
587 auth_key: auth_key_bytes,
588 dh_params: dh.clone(),
589 },
590 ))
591}
592
593fn do_step3(
594 data: Step2,
595 response: ferogram_tl_types::enums::ServerDhParams,
596 random: &[u8; 272],
597 now: i32,
598 retry_id: i64,
599) -> Result<(ferogram_tl_types::functions::SetClientDhParams, Step3), Error> {
600 let Step2 {
601 nonce,
602 server_nonce,
603 new_nonce,
604 } = data;
605
606 let mut server_dh_ok = match response {
607 ferogram_tl_types::enums::ServerDhParams::Fail(f) => {
608 check_nonce(&f.nonce, &nonce)?;
609 check_server_nonce(&f.server_nonce, &server_nonce)?;
610 return Err(Error::DhParamsFail);
611 }
612 ferogram_tl_types::enums::ServerDhParams::Ok(x) => x,
613 };
614
615 check_nonce(&server_dh_ok.nonce, &nonce)?;
616 check_server_nonce(&server_dh_ok.server_nonce, &server_nonce)?;
617
618 if server_dh_ok.encrypted_answer.len() % 16 != 0 {
619 return Err(Error::EncryptedResponseNotPadded {
620 len: server_dh_ok.encrypted_answer.len(),
621 });
622 }
623
624 let (key_arr, iv_arr) = generate_key_data_from_nonce(&server_nonce, &new_nonce);
625 aes::ige_decrypt(&mut server_dh_ok.encrypted_answer, &key_arr, &iv_arr);
626 let plain = server_dh_ok.encrypted_answer;
627
628 let got_hash: [u8; 20] = plain[..20].try_into().unwrap();
629 let mut cursor = Cursor::from_slice(&plain[20..]);
630
631 let inner = match ferogram_tl_types::enums::ServerDhInnerData::deserialize(&mut cursor) {
632 Ok(ferogram_tl_types::enums::ServerDhInnerData::ServerDhInnerData(x)) => x,
633 Err(e) => return Err(Error::InvalidDhInnerData { error: e }),
634 };
635
636 let expected_hash: [u8; 20] = ferogram_crypto::sha1!(&plain[20..20 + cursor.pos()]);
637 if got_hash != expected_hash {
638 return Err(Error::InvalidAnswerHash {
639 got: got_hash,
640 expected: expected_hash,
641 });
642 }
643
644 check_nonce(&inner.nonce, &nonce)?;
645 check_server_nonce(&inner.server_nonce, &server_nonce)?;
646
647 check_p_and_g(&inner.dh_prime, inner.g as u32)
648 .map_err(|source| Error::InvalidDhPrime { source })?;
649
650 let dh_prime_bn = BigUint::from_bytes_be(&inner.dh_prime);
652 let one = BigUint::from(1u32);
653 let g_a_bn = BigUint::from_bytes_be(&inner.g_a);
654 let safety = one.clone() << (2048 - 64);
655 check_g_in_range(&g_a_bn, &safety, &(&dh_prime_bn - &safety))?;
656
657 let dh = DhParamsForRetry {
658 dh_prime: inner.dh_prime,
659 g: inner.g as u32,
660 g_a: inner.g_a,
661 server_time: inner.server_time,
662 aes_key: key_arr,
663 aes_iv: iv_arr,
664 };
665
666 generate_client_dh_params(&dh, nonce, server_nonce, new_nonce, retry_id, random, now)
667}
668
669pub fn finish(
676 data: Step3,
677 response: ferogram_tl_types::enums::SetClientDhParamsAnswer,
678) -> Result<FinishResult, Error> {
679 let Step3 {
680 nonce,
681 server_nonce,
682 new_nonce,
683 time_offset,
684 auth_key: auth_key_bytes,
685 dh_params,
686 } = data;
687
688 struct DhData {
689 nonce: [u8; 16],
690 server_nonce: [u8; 16],
691 hash: [u8; 16],
692 num: u8,
693 }
694
695 let dh = match response {
696 ferogram_tl_types::enums::SetClientDhParamsAnswer::DhGenOk(x) => DhData {
698 nonce: x.nonce,
699 server_nonce: x.server_nonce,
700 hash: x.new_nonce_hash1,
701 num: 1,
702 },
703 ferogram_tl_types::enums::SetClientDhParamsAnswer::DhGenRetry(x) => DhData {
704 nonce: x.nonce,
705 server_nonce: x.server_nonce,
706 hash: x.new_nonce_hash2,
707 num: 2,
708 },
709 ferogram_tl_types::enums::SetClientDhParamsAnswer::DhGenFail(x) => DhData {
710 nonce: x.nonce,
711 server_nonce: x.server_nonce,
712 hash: x.new_nonce_hash3,
713 num: 3,
714 },
715 };
716
717 check_nonce(&dh.nonce, &nonce)?;
718 check_server_nonce(&dh.server_nonce, &server_nonce)?;
719
720 let auth_key = AuthKey::from_bytes(auth_key_bytes);
721 let expected_hash = auth_key.calc_new_nonce_hash(&new_nonce, dh.num);
722 check_new_nonce_hash(&dh.hash, &expected_hash)?;
723
724 let first_salt = {
725 let mut buf = [0u8; 8];
726 for ((dst, a), b) in buf.iter_mut().zip(&new_nonce[..8]).zip(&server_nonce[..8]) {
727 *dst = a ^ b;
728 }
729 i64::from_le_bytes(buf)
730 };
731
732 match dh.num {
733 1 => Ok(FinishResult::Done(Finished {
734 auth_key: auth_key.to_bytes(),
735 time_offset,
736 first_salt,
737 })),
738 2 => {
739 let aux_hash: [u8; 20] = ferogram_crypto::sha1!(auth_key.to_bytes());
741 let retry_id = i64::from_le_bytes(aux_hash[..8].try_into().unwrap());
742 Ok(FinishResult::Retry {
743 retry_id,
744 dh_params,
745 nonce,
746 server_nonce,
747 new_nonce,
748 })
749 }
750 _ => Err(Error::DhGenFail),
751 }
752}
753
754fn check_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
755 if got == expected {
756 Ok(())
757 } else {
758 Err(Error::InvalidNonce {
759 got: *got,
760 expected: *expected,
761 })
762 }
763}
764fn check_server_nonce(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
765 if got == expected {
766 Ok(())
767 } else {
768 Err(Error::InvalidServerNonce {
769 got: *got,
770 expected: *expected,
771 })
772 }
773}
774fn check_new_nonce_hash(got: &[u8; 16], expected: &[u8; 16]) -> Result<(), Error> {
775 if got == expected {
776 Ok(())
777 } else {
778 Err(Error::InvalidNewNonceHash {
779 got: *got,
780 expected: *expected,
781 })
782 }
783}
784fn check_g_in_range(val: &BigUint, lo: &BigUint, hi: &BigUint) -> Result<(), Error> {
785 if lo < val && val < hi {
786 Ok(())
787 } else {
788 Err(Error::GParameterOutOfRange {
789 value: val.clone(),
790 low: lo.clone(),
791 high: hi.clone(),
792 })
793 }
794}
795
796#[allow(clippy::unreadable_literal)]
798pub fn key_for_fingerprint(fp: i64) -> Option<rsa::Key> {
799 Some(match fp {
800 -3414540481677951611 => rsa::Key::new(
802 "29379598170669337022986177149456128565388431120058863768162556424047512191330847455146576344487764408661701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531316010870401287642763009136156734339538042419388722777357134487746169093539093850251243897188928735903389451772730245253062963384108812842079887538976360465290946139638691491496062099570836476454855996319192747663615955633778034897140982517446405334423701359108810182097749467210509584293428076654573384828809574217079944388301239431309115013843331317877374435868468779972014486325557807783825502498215169806323",
803 "65537",
804 )?,
805 -5595554452916591101 => rsa::Key::new(
807 "25342889448840415564971689590713473206898847759084779052582026594546022463853940585885215951168491965708222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166848940585952337613333022396096584117954892216031229237302943701877588456738335398602461675225081791820393153757504952636234951323237820036543581047826906120927972487366805292115792231423684261262330394324750785450942589751755390156647751460719351439969059949569615302809050721500330239005077889855323917509948255722081644689442127297605422579707142646660768825302832201908302295573257427896031830742328565032949",
808 "65537",
809 )?,
810 _ => return None,
811 })
812}