1use super::{Jwk, JwkError};
6
7impl Jwk {
8 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 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
75fn 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
89fn 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
120fn 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
151fn 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 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 assert_eq!(encode(&[0, 0, 0x80, 0x01], &[0, 1, 0, 1]), der);
188 let big = vec![0x7f; 300];
190 let der = encode(&big, &e);
191 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 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 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 assert_eq!(
227 malformed(&[&[0x30, 0x80], body].concat()),
228 "malformed RSAPublicKey: invalid length"
229 );
230 assert_eq!(
232 malformed(&[&[0x30, 0x89, 0x01, 0, 0, 0, 0, 0, 0, 0, 0x80], body].concat()),
233 "malformed RSAPublicKey: invalid length"
234 );
235 #[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 assert_eq!(
243 malformed(&[0x30, 0x82, 0x01]),
244 "malformed RSAPublicKey: invalid length"
245 );
246 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 assert_eq!(
257 malformed(&[0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01]),
258 "malformed RSAPublicKey: empty INTEGER"
259 );
260 }
261}