cmail_rpgp/types/
mpi.rs

1use std::io;
2
3use byteorder::{BigEndian, WriteBytesExt};
4use nom::number::streaming::be_u16;
5use nom::{Err, InputIter, InputTake};
6use num_bigint::BigUint;
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9use crate::errors::{self, Error, IResult};
10use crate::ser::Serialize;
11use crate::util::{bit_size, strip_leading_zeros, strip_leading_zeros_vec};
12
13/// Number of bits we accept when reading or writing MPIs.
14/// The value is the same as gnupgs.
15const MAX_EXTERN_MPI_BITS: u32 = 16384;
16
17/// Parse Multi Precision Integers
18/// Ref: <https://www.rfc-editor.org/rfc/rfc9580.html#name-multiprecision-integers>
19///
20/// # Examples
21///
22/// ```rust
23/// use pgp::types::mpi;
24/// use pgp::types::MpiRef;
25///
26/// // Decode the number `1`.
27/// assert_eq!(
28///     mpi(&[0x00, 0x01, 0x01][..]).unwrap(),
29///     (&b""[..], MpiRef::from_slice(&[1][..]))
30/// );
31/// ```
32pub fn mpi(input: &[u8]) -> IResult<&[u8], MpiRef<'_>> {
33    let (number, len) = be_u16(input)?;
34
35    let bits = u32::from(len);
36    let len_actual = (bits + 7) >> 3;
37
38    if len_actual > MAX_EXTERN_MPI_BITS {
39        Err(Err::Error(Error::InvalidInput))
40    } else {
41        // same as take!
42        let cnt = len_actual as usize;
43        match number.slice_index(cnt) {
44            Err(needed) => Err(nom::Err::Incomplete(needed)),
45            Ok(index) => {
46                let (rest, n) = number.take_split(index);
47                let n_stripped: MpiRef<'_> = MpiRef::from_slice(strip_leading_zeros(n));
48
49                Ok((rest, n_stripped))
50            }
51        }
52    }
53}
54
55/// Represents an owned MPI value.
56/// The inner value is ready to be serialized, without the need to strip leading zeros.
57#[derive(Default, Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop, derive_more::Debug)]
58pub struct Mpi(#[debug("{}", hex::encode(_0))] Vec<u8>);
59
60/// Represents a borrowed MPI value.
61/// The inner value is ready to be serialized, without the need to strip leading zeros.
62#[derive(Clone, PartialEq, Eq, derive_more::Debug)]
63pub struct MpiRef<'a>(#[debug("{}", hex::encode(_0))] &'a [u8]);
64
65impl AsRef<[u8]> for Mpi {
66    fn as_ref(&self) -> &[u8] {
67        self.0.as_ref()
68    }
69}
70
71impl Mpi {
72    pub fn from_raw(mut v: Vec<u8>) -> Self {
73        strip_leading_zeros_vec(&mut v);
74        Mpi(v)
75    }
76
77    /// Strips leading zeros.
78    pub fn from_slice(raw: &[u8]) -> Self {
79        Mpi(strip_leading_zeros(raw).to_vec())
80    }
81
82    pub fn as_ref(&self) -> MpiRef<'_> {
83        MpiRef(&self.0)
84    }
85
86    pub fn as_bytes(&self) -> &[u8] {
87        &self.0
88    }
89}
90
91impl std::ops::Deref for Mpi {
92    type Target = [u8];
93
94    fn deref(&self) -> &Self::Target {
95        &self.0
96    }
97}
98
99impl<'a> std::ops::Deref for MpiRef<'a> {
100    type Target = [u8];
101
102    fn deref(&self) -> &Self::Target {
103        self.0
104    }
105}
106
107impl<'a> MpiRef<'a> {
108    pub fn from_slice(slice: &'a [u8]) -> Self {
109        MpiRef(strip_leading_zeros(slice))
110    }
111
112    pub fn to_owned(&self) -> Mpi {
113        Mpi(self.0.to_owned())
114    }
115
116    pub fn parse(slice: &'a [u8]) -> IResult<&'a [u8], MpiRef<'a>> {
117        mpi(slice)
118    }
119
120    pub fn as_bytes(&self) -> &[u8] {
121        self.0
122    }
123}
124
125impl Serialize for Mpi {
126    fn to_writer<W: io::Write>(&self, w: &mut W) -> errors::Result<()> {
127        MpiRef(&self.0).to_writer(w)
128    }
129}
130
131impl<'a> Serialize for MpiRef<'a> {
132    fn to_writer<W: io::Write>(&self, w: &mut W) -> errors::Result<()> {
133        let bytes = &self.0;
134        let size = bit_size(bytes);
135        w.write_u16::<BigEndian>(size as u16)?;
136        w.write_all(bytes)?;
137
138        Ok(())
139    }
140}
141
142impl From<BigUint> for Mpi {
143    fn from(other: BigUint) -> Self {
144        Mpi(other.to_bytes_be())
145    }
146}
147
148impl From<Mpi> for BigUint {
149    fn from(other: Mpi) -> Self {
150        BigUint::from_bytes_be(other.as_bytes())
151    }
152}
153
154impl<'a> From<&'a Mpi> for BigUint {
155    fn from(other: &'a Mpi) -> Self {
156        BigUint::from_bytes_be(other.as_bytes())
157    }
158}
159
160impl<'a> From<MpiRef<'a>> for BigUint {
161    fn from(other: MpiRef<'a>) -> Self {
162        BigUint::from_bytes_be(other.as_bytes())
163    }
164}
165
166impl<'a, 'b> From<&'b MpiRef<'a>> for BigUint {
167    fn from(other: &'b MpiRef<'a>) -> Self {
168        BigUint::from_bytes_be(other.as_bytes())
169    }
170}
171
172impl<'a> From<&'a BigUint> for Mpi {
173    fn from(other: &'a BigUint) -> Self {
174        Mpi(other.to_bytes_be())
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    #![allow(clippy::unwrap_used)]
181
182    use super::*;
183
184    #[test]
185    fn test_mpi() {
186        // Decode the number `511` (`0x1FF` in hex).
187        assert_eq!(
188            mpi(&[0x00, 0x09, 0x01, 0xFF][..]).unwrap(),
189            (&b""[..], MpiRef::from_slice(&[0x01, 0xFF][..]))
190        );
191
192        // Decode the number `2^255 + 7`.
193        assert_eq!(
194            mpi(&[
195                0x01, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
196                0, 0, 0, 0, 0, 0, 0, 0x07
197            ][..])
198            .unwrap(),
199            (
200                &b""[..],
201                MpiRef::from_slice(
202                    &[
203                        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
204                        0, 0, 0, 0, 0, 0, 0, 0x07
205                    ][..]
206                )
207            )
208        );
209    }
210
211    #[test]
212    fn test_bignum_mpi() {
213        let fixtures = [
214            ("b4a71b058ac8aa1ddc453ab2663331c38f7645542815ac189a9af56d0e07a615469d3e08849650e03026d49259423cf00d089931cd700fd3a6e940bf83c81406e142a4b0a86f00738c7e1a9ff1b709f6bccc6cf900d0113a8e62e53d63be0a05105755b9efc6a4098c362c73fb422d40187d8e2382e88624d72caffceb13cec8fa0079c7d17883a46a1336471ab5be8cbb555c5d330d7fadb43318fa73b584edac312fa3302886bb5d04a05da3be2676c1fb94b3cf5c19d598659c3a7728ebab95f71721b662ac46aa9910726fe576d438f789c5ce2448f54546f254da814bcae1c35ee44b171e870ffa6403167a10e68573bdf155549274b431ff8e2418b627", "0800b4a71b058ac8aa1ddc453ab2663331c38f7645542815ac189a9af56d0e07a615469d3e08849650e03026d49259423cf00d089931cd700fd3a6e940bf83c81406e142a4b0a86f00738c7e1a9ff1b709f6bccc6cf900d0113a8e62e53d63be0a05105755b9efc6a4098c362c73fb422d40187d8e2382e88624d72caffceb13cec8fa0079c7d17883a46a1336471ab5be8cbb555c5d330d7fadb43318fa73b584edac312fa3302886bb5d04a05da3be2676c1fb94b3cf5c19d598659c3a7728ebab95f71721b662ac46aa9910726fe576d438f789c5ce2448f54546f254da814bcae1c35ee44b171e870ffa6403167a10e68573bdf155549274b431ff8e2418b627"),
215            ("00e57192fa7bd6abd7d01331f0411eebff4651290af1329369cc3bb3b8ccbd7ba6e352400c3f64f637967e24524921ee04f1e0a79168781f0bec9029e34c8a1fb1c328a4b8d74c31429616a6ff4707bb56b71ab66643243087c8ff0d0c4883b3473c56deece9a83dbd06eef09fac3558003ae45f8898b8a9490aa79672eebdd7d985d051d62698f2da7eee33ba740e30fc5a93c3f16ca1490dfd62b84ba016c9da7c087a28a4e97d8af79c6b638bc22f20a8b5953bb83caa3dddaaf1d0dc15a3f7ed47870174af74e5308b856138771a10019fe4374389eb89d2280776e33fa2dd3526cec35cd86a9cf6c94253fe00c4b8a87a36451745116456833bb1a237", "07f0e57192fa7bd6abd7d01331f0411eebff4651290af1329369cc3bb3b8ccbd7ba6e352400c3f64f637967e24524921ee04f1e0a79168781f0bec9029e34c8a1fb1c328a4b8d74c31429616a6ff4707bb56b71ab66643243087c8ff0d0c4883b3473c56deece9a83dbd06eef09fac3558003ae45f8898b8a9490aa79672eebdd7d985d051d62698f2da7eee33ba740e30fc5a93c3f16ca1490dfd62b84ba016c9da7c087a28a4e97d8af79c6b638bc22f20a8b5953bb83caa3dddaaf1d0dc15a3f7ed47870174af74e5308b856138771a10019fe4374389eb89d2280776e33fa2dd3526cec35cd86a9cf6c94253fe00c4b8a87a36451745116456833bb1a237"),
216        ];
217
218        for (i, (raw, encoded)) in fixtures.iter().enumerate() {
219            println!("fixture {i}");
220            let n = hex::decode(raw).unwrap();
221
222            let n_big = BigUint::from_bytes_be(&n);
223            let n_mpi: Mpi = n_big.clone().into();
224            let mut n_encoded = Vec::new();
225            n_mpi.to_writer(&mut n_encoded).unwrap();
226
227            assert_eq!(&n_encoded, &hex::decode(encoded).unwrap());
228
229            let (rest, n_big2) = mpi(&n_encoded).unwrap();
230            assert_eq!(rest.len(), 0);
231            assert_eq!(n_big, n_big2.into());
232        }
233    }
234}