1use base64::prelude::*;
20use rand::prelude::*;
21use rug::Integer;
22use rug::integer::Order;
23use rug::ops::Pow;
24use std::convert::TryInto;
25use std::fmt;
26
27const VERSION: &str = "s";
28
29#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
34pub struct ChallengeParams {
35 pub difficulty: u32,
37 pub val: Integer,
39}
40
41fn square_mod(n: &mut Integer) {
43 n.square_mut();
44 let high = Integer::from(&*n >> 1279);
45 n.keep_bits_mut(1279);
46 *n += high;
47 if n.get_bit(1279) {
48 n.set_bit(1279, false);
49 *n += 1;
50 }
51}
52
53impl ChallengeParams {
54 pub fn decode_challenge(chall_string: &str) -> Result<ChallengeParams, &'static str> {
59 let mut parts = chall_string.split('.');
60 if parts.next() != Some(VERSION) {
61 return Err("Incorrect version");
62 }
63 let data: Vec<_> = parts.collect();
64 if data.len() != 2 {
65 return Err("Incorrect number of parts");
66 }
67 let decoded_data: Vec<_> = data
68 .into_iter()
69 .map(|x| {
70 BASE64_STANDARD
71 .decode(x)
72 .map_err(|_| "Parts aren't valid base64")
73 })
74 .collect::<Result<_, _>>()?;
75 let difficulty_bytes = &decoded_data[0];
76 let difficulty: u32 = if difficulty_bytes.len() > 4 {
77 let (first, last) = difficulty_bytes.split_at(difficulty_bytes.len() - 4);
78 if first.iter().any(|&x| x != 0) {
80 return Err("Difficulty is too large");
81 }
82 u32::from_be_bytes(last.try_into().unwrap())
83 } else {
84 let mut difficulty_array = [0; 4];
85 difficulty_array[4 - difficulty_bytes.len()..].copy_from_slice(difficulty_bytes);
86 u32::from_be_bytes(difficulty_array)
87 };
88 Ok(Self {
89 val: Integer::from_digits(&decoded_data[1], Order::Msf),
90 difficulty,
91 })
92 }
93
94 pub fn generate_challenge(difficulty: u32) -> ChallengeParams {
96 let mut bytes: [u8; 16] = [0; 16];
97 rand::rng().fill(&mut bytes[..]);
98 Self {
99 val: Integer::from_digits(&bytes, Order::Msf),
100 difficulty,
101 }
102 }
103
104 pub fn solve(mut self) -> String {
106 for _ in 0..self.difficulty {
107 for _ in 0..1277 {
109 square_mod(&mut self.val);
110 }
111 self.val ^= 1;
112 }
113 format!(
114 "{}.{}",
115 VERSION,
116 BASE64_STANDARD.encode(self.val.to_digits(Order::Msf))
117 )
118 }
119
120 pub fn check(&self, sol: &str) -> Result<bool, &'static str> {
122 let mut parts = sol.split('.');
123 if parts.next() != Some(VERSION) {
124 return Err("Incorrect version");
125 }
126 let Some(data) = parts.next() else {
127 return Err("Incorrect number of parts");
128 };
129 if parts.next().is_some() {
130 return Err("Incorrect number of parts");
131 }
132 let decoded_data = BASE64_STANDARD
133 .decode(data)
134 .map_err(|_| "Parts aren't valid base64")?;
135 let mut sol_val = Integer::from_digits(&decoded_data, Order::Msf);
136 for _ in 0..self.difficulty {
137 sol_val ^= 1;
138 square_mod(&mut sol_val);
139 }
140 Ok(self.val == sol_val || Integer::from(2).pow(1279) - 1 - &self.val == sol_val)
141 }
142}
143
144impl fmt::Display for ChallengeParams {
145 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
146 write!(
147 fmt,
148 "{}.{}.{}",
149 VERSION,
150 BASE64_STANDARD.encode(self.difficulty.to_be_bytes()),
151 BASE64_STANDARD.encode(self.val.to_digits(Order::Msf))
152 )
153 }
154}