Skip to main content

identity_kit/jose/
pkcs1.rs

1//! PKCS #1 `RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER }` (RFC 8017
2//! Appendix A.1.1) in DER: the form in which some vaults and HSMs return an RSA public key, and
3//! the raw key of an `rsa-pub` Multikey.
4
5use super::{Jwk, JwkError};
6
7impl Jwk {
8    /// An `RSA` key from a DER-encoded PKCS #1 `RSAPublicKey`
9    /// (`SEQUENCE { modulus INTEGER, publicExponent INTEGER }`, RFC 8017 Appendix A.1.1).
10    ///
11    /// This is for key store backends whose vault returns an RSA public key as PKCS #1 rather
12    /// than as a `SubjectPublicKeyInfo` (for that, see [`Jwk::from_spki_der`]). The parser is
13    /// strict DER: definite lengths in the fewest octets, non-negative minimally encoded
14    /// INTEGERs, nothing after the `SEQUENCE`. It needs no Cargo feature.
15    ///
16    /// Unlike [`Jwk::from_spki_der`], it takes no [`KeyType`](super::KeyType) and does not check
17    /// the modulus size, as [`Jwk::from_rsa_public`] and [`Jwk::from_multikey`] do not: PKCS #1
18    /// carries no algorithm identifier for a hint to select a parser by, and
19    /// [`DefaultVerifier`](super::DefaultVerifier) rejects an unusable modulus, or one shorter
20    /// than 2048 bits, when the key is used.
21    ///
22    /// # Errors
23    ///
24    /// [`JwkError::InvalidKey`] with `key: "RSA"` when `der` is not a DER `RSAPublicKey`.
25    ///
26    /// # Examples
27    ///
28    /// ```
29    /// use identity_kit::jose::Jwk;
30    ///
31    /// // `SEQUENCE { INTEGER 0x00c5, INTEGER 65537 }`: the leading zero keeps the modulus positive.
32    /// let der = [0x30, 0x09, 0x02, 0x02, 0x00, 0xc5, 0x02, 0x03, 0x01, 0x00, 0x01];
33    /// let jwk = Jwk::from_pkcs1_der(&der)?;
34    /// assert_eq!(jwk.rsa_public_components()?, (vec![0xc5], vec![0x01, 0x00, 0x01]));
35    /// assert_eq!(jwk.to_pkcs1_der()?, der);
36    /// # Ok::<(), identity_kit::jose::JwkError>(())
37    /// ```
38    pub fn from_pkcs1_der(der: &[u8]) -> Result<Self, JwkError> {
39        let (n, e) = decode(der)?;
40        Self::from_rsa_public(&n, &e)
41    }
42
43    /// This `RSA` key's public parameters as a DER-encoded PKCS #1 `RSAPublicKey`
44    /// (RFC 8017 Appendix A.1.1), for key store backends whose vault takes that form.
45    ///
46    /// The encoding is DER, so it is the same for every JWK of the same key.
47    ///
48    /// # Errors
49    ///
50    /// The errors of [`Jwk::rsa_public_components`]: [`JwkError::UnexpectedKeyType`] for any
51    /// other key, [`JwkError::MissingMember`] or [`JwkError::InvalidBase64Url`] for a broken `n`
52    /// or `e`; [`JwkError::InvalidKey`] when `n` or `e` is zero, which no `RSAPublicKey` that
53    /// [`Jwk::from_pkcs1_der`] reads can hold.
54    pub fn to_pkcs1_der(&self) -> Result<Vec<u8>, JwkError> {
55        let (n, e) = self.rsa_public_components()?;
56        if [&n, &e].iter().any(|value| value.iter().all(|&b| b == 0)) {
57            return Err(JwkError::InvalidKey {
58                key: "RSA",
59                reason: "modulus and exponent must not be zero".to_owned(),
60            });
61        }
62        Ok(encode(&n, &e))
63    }
64}
65
66const SEQUENCE: u8 = 0x30;
67const INTEGER: u8 = 0x02;
68
69fn encode(n: &[u8], e: &[u8]) -> Vec<u8> {
70    let mut body = integer(n);
71    body.extend(integer(e));
72    tlv(SEQUENCE, &body)
73}
74
75/// The big-endian `n` and `e` of `der`, without leading zero octets.
76fn decode(der: &[u8]) -> Result<(Vec<u8>, Vec<u8>), JwkError> {
77    let (body, rest) = read_tlv(der, SEQUENCE)?;
78    if !rest.is_empty() {
79        return Err(invalid("trailing bytes after RSAPublicKey"));
80    }
81    let (n, body) = read_tlv(body, INTEGER)?;
82    let (e, body) = read_tlv(body, INTEGER)?;
83    if !body.is_empty() {
84        return Err(invalid("trailing bytes inside RSAPublicKey"));
85    }
86    Ok((unsigned(n)?.to_vec(), unsigned(e)?.to_vec()))
87}
88
89/// A DER INTEGER holding the unsigned big-endian `value`.
90fn integer(value: &[u8]) -> Vec<u8> {
91    let first = value.iter().position(|&b| b != 0).unwrap_or(value.len());
92    let trimmed = &value[first..];
93    let mut body = Vec::with_capacity(trimmed.len() + 1);
94    if trimmed.first().is_none_or(|&b| b & 0x80 != 0) {
95        body.push(0);
96    }
97    body.extend_from_slice(trimmed);
98    tlv(INTEGER, &body)
99}
100
101#[expect(
102    clippy::cast_possible_truncation,
103    reason = "a short-form length is below 0x80, and a long form has at most 8 length octets"
104)]
105fn tlv(tag: u8, body: &[u8]) -> Vec<u8> {
106    let mut out = vec![tag];
107    let len = body.len();
108    if len < 0x80 {
109        out.push(len as u8);
110    } else {
111        let be = len.to_be_bytes();
112        let first = be.iter().position(|&b| b != 0).unwrap_or(be.len() - 1);
113        out.push(0x80 | (be.len() - first) as u8);
114        out.extend_from_slice(&be[first..]);
115    }
116    out.extend_from_slice(body);
117    out
118}
119
120/// The body of the TLV at the start of `der`, which must have tag `tag`, and what follows.
121fn read_tlv(der: &[u8], tag: u8) -> Result<(&[u8], &[u8]), JwkError> {
122    let (&found, rest) = der.split_first().ok_or_else(|| invalid("unexpected end"))?;
123    if found != tag {
124        return Err(invalid("unexpected ASN.1 tag"));
125    }
126    let (&first, rest) = rest
127        .split_first()
128        .ok_or_else(|| invalid("unexpected end"))?;
129    let (len, rest) = if first < 0x80 {
130        (usize::from(first), rest)
131    } else {
132        let count = usize::from(first & 0x7f);
133        if count == 0 || count > size_of::<usize>() || rest.len() < count {
134            return Err(invalid("invalid length"));
135        }
136        let (len_bytes, rest) = rest.split_at(count);
137        if len_bytes[0] == 0 || (count == 1 && len_bytes[0] < 0x80) {
138            return Err(invalid("length is not minimally encoded"));
139        }
140        let len = len_bytes
141            .iter()
142            .fold(0usize, |acc, &b| (acc << 8) | usize::from(b));
143        (len, rest)
144    };
145    if rest.len() < len {
146        return Err(invalid("length exceeds input"));
147    }
148    Ok(rest.split_at(len))
149}
150
151/// The magnitude of a non-negative, minimally encoded INTEGER body.
152fn unsigned(body: &[u8]) -> Result<&[u8], JwkError> {
153    match body {
154        [] => Err(invalid("empty INTEGER")),
155        [first, ..] if first & 0x80 != 0 => Err(invalid("negative INTEGER")),
156        [0, second, ..] if second & 0x80 == 0 => Err(invalid("INTEGER has a leading zero")),
157        [0, rest @ ..] => Ok(rest),
158        _ => Ok(body),
159    }
160}
161
162fn invalid(reason: &str) -> JwkError {
163    JwkError::InvalidKey {
164        key: "RSA",
165        reason: format!("malformed RSAPublicKey: {reason}"),
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn pkcs1_round_trips_and_is_minimal() {
175        // RFC 8017: INTEGERs are two's complement, so a high bit needs a leading zero.
176        let n = [0x80, 0x01];
177        let e = [0x01, 0x00, 0x01];
178        let der = encode(&n, &e);
179        assert_eq!(
180            der,
181            [
182                0x30, 0x0a, 0x02, 0x03, 0x00, 0x80, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01
183            ]
184        );
185        assert_eq!(decode(&der).unwrap(), (n.to_vec(), e.to_vec()));
186        // Leading zeros of the input are dropped.
187        assert_eq!(encode(&[0, 0, 0x80, 0x01], &[0, 1, 0, 1]), der);
188        // Long-form lengths.
189        let big = vec![0x7f; 300];
190        let der = encode(&big, &e);
191        // 1 + 3 + 300 bytes of modulus INTEGER plus 5 of exponent: 309 = 0x135.
192        assert_eq!(&der[..4], [0x30, 0x82, 0x01, 0x35]);
193        assert_eq!(decode(&der).unwrap().0, big);
194        for broken in [
195            &[0x30, 0x00][..],
196            &[0x30, 0x04, 0x02, 0x01, 0xff, 0x02][..],
197            &[0x30, 0x06, 0x02, 0x01, 0x80, 0x02, 0x01, 0x01][..],
198            &[0x30, 0x07, 0x02, 0x02, 0x00, 0x01, 0x02, 0x01, 0x01][..],
199            &[0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00][..],
200            &[0x30, 0x81, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01][..],
201        ] {
202            assert!(decode(broken).is_err(), "{broken:02x?}");
203        }
204    }
205
206    /// Why `der` is not an `RSAPublicKey`.
207    fn malformed(der: &[u8]) -> String {
208        match decode(der) {
209            Err(JwkError::InvalidKey { key: "RSA", reason }) => reason,
210            other => panic!("{der:02x?}: {other:?}"),
211        }
212    }
213
214    #[test]
215    fn pkcs1_lengths_are_strict_der() {
216        // X.690 §10.1: definite lengths in the fewest octets, the long form from 128 on. A body of
217        // exactly 128 bytes: 2 + 121 bytes of modulus INTEGER plus 5 of exponent.
218        let n = vec![0x7f; 121];
219        let e = [0x01, 0x00, 0x01];
220        let der = encode(&n, &e);
221        assert_eq!(&der[..3], [0x30, 0x81, 0x80]);
222        assert_eq!(decode(&der).unwrap(), (n, e.to_vec()));
223        let body = &der[3..];
224
225        // `0x80` is the indefinite length of BER, which DER does not allow.
226        assert_eq!(
227            malformed(&[&[0x30, 0x80], body].concat()),
228            "malformed RSAPublicKey: invalid length"
229        );
230        // Nine length octets, more than a `usize` holds, although their value is 128.
231        assert_eq!(
232            malformed(&[&[0x30, 0x89, 0x01, 0, 0, 0, 0, 0, 0, 0, 0x80], body].concat()),
233            "malformed RSAPublicKey: invalid length"
234        );
235        // Eight length octets fit a 64-bit `usize`; 2^56 bytes are not there.
236        #[cfg(target_pointer_width = "64")]
237        assert_eq!(
238            malformed(&[0x30, 0x88, 0x01, 0, 0, 0, 0, 0, 0, 0]),
239            "malformed RSAPublicKey: length exceeds input"
240        );
241        // Fewer length octets than announced.
242        assert_eq!(
243            malformed(&[0x30, 0x82, 0x01]),
244            "malformed RSAPublicKey: invalid length"
245        );
246        // The length octets, but no body.
247        assert_eq!(
248            malformed(&[0x30, 0x81, 0x80]),
249            "malformed RSAPublicKey: length exceeds input"
250        );
251    }
252
253    #[test]
254    fn pkcs1_rejects_an_empty_integer() {
255        // X.690 §8.3.1: the contents of an INTEGER are one or more octets.
256        assert_eq!(
257            malformed(&[0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01]),
258            "malformed RSAPublicKey: empty INTEGER"
259        );
260    }
261}