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