gcrypt/mpi/
integer.rs

1use std::{cmp::Ordering, fmt, ops, ptr, str};
2
3use cstr_argument::CStrArgument;
4use ffi;
5use libc::c_uint;
6
7use crate::{buffer::Buffer, error::return_err, rand::Level, Error, NonNull, Result};
8
9#[repr(usize)]
10#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
11pub enum Format {
12    Standard = ffi::GCRYMPI_FMT_STD as usize,
13    Unsigned = ffi::GCRYMPI_FMT_USG as usize,
14    Pgp = ffi::GCRYMPI_FMT_PGP as usize,
15    Ssh = ffi::GCRYMPI_FMT_SSH as usize,
16    Hex = ffi::GCRYMPI_FMT_HEX as usize,
17}
18
19pub struct Integer(NonNull<ffi::gcry_mpi_t>);
20
21impl Drop for Integer {
22    #[inline]
23    fn drop(&mut self) {
24        unsafe {
25            ffi::gcry_mpi_release(self.as_raw());
26        }
27    }
28}
29
30impl Clone for Integer {
31    #[inline]
32    fn clone(&self) -> Integer {
33        unsafe { Integer::from_raw(ffi::gcry_mpi_copy(self.as_raw())) }
34    }
35
36    #[inline]
37    fn clone_from(&mut self, source: &Integer) {
38        unsafe {
39            ffi::gcry_mpi_set(self.as_raw(), source.as_raw());
40        }
41    }
42}
43
44impl Integer {
45    impl_wrapper!(Integer: ffi::gcry_mpi_t);
46
47    #[inline]
48    pub fn zero() -> Integer {
49        Integer::new(0)
50    }
51
52    #[inline]
53    pub fn one() -> Integer {
54        Integer::from_uint(1)
55    }
56
57    #[inline]
58    pub fn new(nbits: u32) -> Integer {
59        let _ = crate::init_default();
60        unsafe { Integer::from_raw(ffi::gcry_mpi_new(nbits.into())) }
61    }
62
63    #[inline]
64    pub fn new_secure(nbits: u32) -> Integer {
65        let _ = crate::init_default();
66        unsafe { Integer::from_raw(ffi::gcry_mpi_snew(nbits.into())) }
67    }
68
69    #[inline]
70    pub fn from_uint(n: u32) -> Integer {
71        let _ = crate::init_default();
72        unsafe { Integer::from_raw(ffi::gcry_mpi_set_ui(ptr::null_mut(), n.into())) }
73    }
74
75    #[inline]
76    pub fn from_bytes(format: Format, bytes: impl AsRef<[u8]>) -> Result<Integer> {
77        let bytes = bytes.as_ref();
78        let _ = crate::init_default();
79        unsafe {
80            let mut raw = ptr::null_mut();
81            let len = if format != Format::Hex {
82                bytes.len()
83            } else if bytes.contains(&0) {
84                0
85            } else {
86                return Err(Error::INV_ARG);
87            };
88            return_err!(ffi::gcry_mpi_scan(
89                &mut raw,
90                format as ffi::gcry_mpi_format,
91                bytes.as_ptr().cast(),
92                len,
93                ptr::null_mut()
94            ));
95            Ok(Integer::from_raw(raw))
96        }
97    }
98
99    #[inline]
100    pub fn from_str(s: impl CStrArgument) -> Result<Integer> {
101        let s = s.into_cstr();
102        Integer::from_bytes(Format::Hex, s.as_ref().to_bytes_with_nul())
103    }
104
105    #[inline]
106    pub fn to_bytes(&self, format: Format) -> Result<Buffer> {
107        unsafe {
108            let mut buffer = ptr::null_mut();
109            let mut len = 0;
110            return_err!(ffi::gcry_mpi_aprint(
111                format as ffi::gcry_mpi_format,
112                &mut buffer,
113                &mut len,
114                self.as_raw()
115            ));
116            Ok(Buffer::from_raw(buffer.cast(), len))
117        }
118    }
119
120    #[inline]
121    pub fn len_encoded(&self, format: Format) -> Result<usize> {
122        unsafe {
123            let mut len = 0;
124            return_err!(ffi::gcry_mpi_print(
125                format as ffi::gcry_mpi_format,
126                ptr::null_mut(),
127                0,
128                &mut len,
129                self.as_raw()
130            ));
131            Ok(len)
132        }
133    }
134
135    #[inline]
136    pub fn encode(&self, format: Format, buf: &mut [u8]) -> Result<usize> {
137        unsafe {
138            let mut written = 0;
139            return_err!(ffi::gcry_mpi_print(
140                format as ffi::gcry_mpi_format,
141                buf.as_mut_ptr().cast(),
142                buf.len(),
143                &mut written,
144                self.as_raw()
145            ));
146            Ok(written)
147        }
148    }
149
150    #[inline]
151    pub fn set(&mut self, n: u32) {
152        unsafe {
153            ffi::gcry_mpi_set_ui(self.as_raw(), n.into());
154        }
155    }
156
157    #[inline]
158    pub fn randomize(&mut self, nbits: u32, level: Level) {
159        unsafe {
160            ffi::gcry_mpi_randomize(self.as_raw(), nbits.into(), level.raw());
161        }
162    }
163
164    #[inline]
165    pub fn num_bits(&self) -> usize {
166        unsafe { ffi::gcry_mpi_get_nbits(self.as_raw()) as usize }
167    }
168
169    #[inline]
170    pub fn is_prime(&self) -> bool {
171        unsafe { ffi::gcry_prime_check(self.as_raw(), 0) == 0 }
172    }
173
174    #[inline]
175    pub fn is_positive(&self) -> bool {
176        unsafe { ffi::gcry_mpi_cmp_ui(self.as_raw(), 0) > 0 }
177    }
178
179    #[inline]
180    pub fn is_negative(&self) -> bool {
181        unsafe { ffi::gcry_mpi_cmp_ui(self.as_raw(), 0) < 0 }
182    }
183
184    #[inline]
185    pub fn abs(self) -> Integer {
186        unsafe {
187            ffi::gcry_mpi_abs(self.as_raw());
188        }
189        self
190    }
191
192    #[inline]
193    pub fn add_mod(self, other: &Integer, m: &Integer) -> Integer {
194        unsafe {
195            ffi::gcry_mpi_addm(self.as_raw(), self.as_raw(), other.as_raw(), m.as_raw());
196        }
197        self
198    }
199
200    #[inline]
201    pub fn sub_mod(self, other: &Integer, m: &Integer) -> Integer {
202        unsafe {
203            ffi::gcry_mpi_subm(self.as_raw(), self.as_raw(), other.as_raw(), m.as_raw());
204        }
205        self
206    }
207
208    #[inline]
209    pub fn mul_mod(self, other: &Integer, m: &Integer) -> Integer {
210        unsafe {
211            ffi::gcry_mpi_mulm(self.as_raw(), self.as_raw(), other.as_raw(), m.as_raw());
212        }
213        self
214    }
215
216    #[inline]
217    pub fn inv_mod(self, m: &Integer) -> Option<Integer> {
218        let result = unsafe { ffi::gcry_mpi_invm(self.as_raw(), self.as_raw(), m.as_raw()) };
219        if result != 0 {
220            Some(self)
221        } else {
222            None
223        }
224    }
225
226    #[inline]
227    pub fn div_floor(self, other: &Integer) -> Integer {
228        unsafe {
229            ffi::gcry_mpi_div(
230                self.as_raw(),
231                ptr::null_mut(),
232                self.as_raw(),
233                other.as_raw(),
234                -1,
235            );
236        }
237        self
238    }
239
240    #[inline]
241    pub fn mod_floor(self, other: &Integer) -> Integer {
242        unsafe {
243            ffi::gcry_mpi_div(
244                ptr::null_mut(),
245                self.as_raw(),
246                self.as_raw(),
247                other.as_raw(),
248                -1,
249            );
250        }
251        self
252    }
253
254    #[inline]
255    pub fn div_rem(self, other: &Integer) -> (Integer, Integer) {
256        let rem = Integer::zero();
257        unsafe {
258            ffi::gcry_mpi_div(
259                self.as_raw(),
260                rem.as_raw(),
261                self.as_raw(),
262                other.as_raw(),
263                0,
264            );
265        }
266        (self, rem)
267    }
268
269    #[inline]
270    pub fn div_mod_floor(self, other: &Integer) -> (Integer, Integer) {
271        let rem = Integer::zero();
272        unsafe {
273            ffi::gcry_mpi_div(
274                self.as_raw(),
275                rem.as_raw(),
276                self.as_raw(),
277                other.as_raw(),
278                -1,
279            );
280        }
281        (self, rem)
282    }
283
284    #[inline]
285    pub fn ldexp(self, e: u32) -> Integer {
286        unsafe {
287            ffi::gcry_mpi_mul_2exp(self.as_raw(), self.as_raw(), e.into());
288        }
289        self
290    }
291
292    #[inline]
293    pub fn pow_mod(self, e: &Integer, m: &Integer) -> Integer {
294        unsafe {
295            ffi::gcry_mpi_powm(self.as_raw(), self.as_raw(), e.as_raw(), m.as_raw());
296        }
297        self
298    }
299
300    #[inline]
301    pub fn gcd(self, other: &Integer) -> Integer {
302        unsafe {
303            ffi::gcry_mpi_gcd(self.as_raw(), self.as_raw(), other.as_raw());
304        }
305        self
306    }
307
308    #[inline]
309    pub fn lcm(self, other: &Integer) -> Integer {
310        (self.clone() * other) / self.gcd(other)
311    }
312}
313
314impl Default for Integer {
315    #[inline]
316    fn default() -> Self {
317        Self::zero()
318    }
319}
320
321impl From<u32> for Integer {
322    #[inline]
323    fn from(x: u32) -> Self {
324        Self::from_uint(x)
325    }
326}
327
328impl fmt::Debug for Integer {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        let mut s = f.debug_struct("Integer");
331        s.field("raw", &self.0);
332        if let Ok(bytes) = self.to_bytes(Format::Hex) {
333            s.field("hex", &str::from_utf8(&bytes[..(bytes.len() - 1)]).unwrap());
334        }
335        s.finish()
336    }
337}
338
339impl PartialEq<u32> for Integer {
340    #[inline]
341    fn eq(&self, other: &u32) -> bool {
342        self.partial_cmp(other).unwrap() == Ordering::Equal
343    }
344}
345
346impl PartialEq<Integer> for u32 {
347    #[inline]
348    fn eq(&self, other: &Integer) -> bool {
349        self.partial_cmp(other).unwrap() == Ordering::Equal
350    }
351}
352
353impl PartialOrd<u32> for Integer {
354    #[inline]
355    fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
356        let result = unsafe { ffi::gcry_mpi_cmp_ui(self.as_raw(), (*other).into()) };
357        match result {
358            x if x < 0 => Some(Ordering::Less),
359            x if x > 0 => Some(Ordering::Greater),
360            _ => Some(Ordering::Equal),
361        }
362    }
363}
364
365impl PartialOrd<Integer> for u32 {
366    #[inline]
367    fn partial_cmp(&self, other: &Integer) -> Option<Ordering> {
368        other.partial_cmp(self).map(Ordering::reverse)
369    }
370}
371
372impl PartialEq for Integer {
373    #[inline]
374    fn eq(&self, other: &Integer) -> bool {
375        self.cmp(other) == Ordering::Equal
376    }
377}
378impl Eq for Integer {}
379
380impl PartialOrd for Integer {
381    #[inline]
382    fn partial_cmp(&self, other: &Integer) -> Option<Ordering> {
383        Some(self.cmp(other))
384    }
385}
386impl Ord for Integer {
387    #[inline]
388    fn cmp(&self, other: &Integer) -> Ordering {
389        let result = unsafe { ffi::gcry_mpi_cmp(self.as_raw(), other.as_raw()) };
390        match result {
391            x if x < 0 => Ordering::Less,
392            x if x > 0 => Ordering::Greater,
393            _ => Ordering::Equal,
394        }
395    }
396}
397
398impl ops::Neg for Integer {
399    type Output = Integer;
400
401    #[inline]
402    fn neg(self) -> Integer {
403        unsafe {
404            ffi::gcry_mpi_neg(self.as_raw(), self.as_raw());
405        }
406        self
407    }
408}
409
410impl ops::Neg for &'_ Integer {
411    type Output = Integer;
412
413    #[inline]
414    fn neg(self) -> Integer {
415        self.clone().neg()
416    }
417}
418
419macro_rules! impl_binary_op {
420    ($imp:ident, $method:ident, $body:expr) => {
421        impl ops::$imp for Integer {
422            type Output = Integer;
423
424            #[inline]
425            fn $method(self, other: Integer) -> Integer {
426                self.$method(&other)
427            }
428        }
429        impl<'a> ops::$imp<Integer> for &'a Integer {
430            type Output = Integer;
431
432            #[inline]
433            fn $method(self, other: Integer) -> Integer {
434                self.clone().$method(&other)
435            }
436        }
437
438        impl<'a> ops::$imp<&'a Integer> for Integer {
439            type Output = Integer;
440
441            #[inline]
442            fn $method(self, other: &'a Integer) -> Integer {
443                unsafe {
444                    $body(self.as_raw(), other.as_raw());
445                }
446                self
447            }
448        }
449    };
450}
451impl_binary_op!(Add, add, |x, y| ffi::gcry_mpi_add(x, x, y));
452impl_binary_op!(Sub, sub, |x, y| ffi::gcry_mpi_sub(x, x, y));
453impl_binary_op!(Mul, mul, |x, y| ffi::gcry_mpi_mul(x, x, y));
454impl_binary_op!(Div, div, |x, y| ffi::gcry_mpi_div(
455    x,
456    ptr::null_mut(),
457    x,
458    y,
459    0
460));
461impl_binary_op!(Rem, rem, |x, y| ffi::gcry_mpi_mod(x, x, y));
462
463impl ops::Shl<usize> for Integer {
464    type Output = Integer;
465
466    #[inline]
467    fn shl(self, other: usize) -> Integer {
468        unsafe {
469            ffi::gcry_mpi_lshift(self.as_raw(), self.as_raw(), other as c_uint);
470        }
471        self
472    }
473}
474
475impl ops::Shr<usize> for Integer {
476    type Output = Integer;
477
478    #[inline]
479    fn shr(self, other: usize) -> Integer {
480        unsafe {
481            ffi::gcry_mpi_rshift(self.as_raw(), self.as_raw(), other as c_uint);
482        }
483        self
484    }
485}