Skip to main content

cryptography/public_key/
rsa_io.rs

1//! Modern RSA key externalization helpers.
2//!
3//! The raw `Rsa` primitive stores just enough information to derive standards
4//! formats on demand:
5//! - `SubjectPublicKeyInfo` (SPKI) for public keys
6//! - `PKCS #8` (`PrivateKeyInfo`) for private keys
7//!
8//! Lower-level PKCS #1 RSA key structures are also exposed because they are the
9//! inner payloads of those modern containers and remain useful for debugging or
10//! interop with older tooling.
11
12use crate::public_key::bigint::BigUint;
13use crate::public_key::io::{pem_unwrap, pem_wrap, xml_unwrap, xml_wrap};
14use crate::public_key::rsa::{Rsa, RsaPrivateKey, RsaPublicKey};
15
16const RSA_ENCRYPTION_OID: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
17
18impl RsaPublicKey {
19    /// Encode the public key as the PKCS #1 `RSAPublicKey` structure in DER.
20    #[must_use]
21    pub fn to_pkcs1_der(&self) -> Vec<u8> {
22        let mut body = Vec::new();
23        body.extend(der_integer_biguint(self.modulus()));
24        body.extend(der_integer_biguint(self.exponent()));
25        der_sequence(&body)
26    }
27
28    /// Encode the public key as `SubjectPublicKeyInfo` in DER.
29    #[must_use]
30    pub fn to_spki_der(&self) -> Vec<u8> {
31        let pkcs1 = self.to_pkcs1_der();
32        let mut alg = Vec::new();
33        alg.extend(der_oid(RSA_ENCRYPTION_OID));
34        alg.extend(der_null());
35
36        let mut body = Vec::new();
37        body.extend(der_sequence(&alg));
38        body.extend(der_bit_string(&pkcs1));
39        der_sequence(&body)
40    }
41
42    /// Encode the public key as the PKCS #1 `RSA PUBLIC KEY` PEM label.
43    #[must_use]
44    pub fn to_pkcs1_pem(&self) -> String {
45        pem_wrap("RSA PUBLIC KEY", &self.to_pkcs1_der())
46    }
47
48    /// Encode the public key as `PUBLIC KEY` PEM (`SubjectPublicKeyInfo`).
49    #[must_use]
50    pub fn to_spki_pem(&self) -> String {
51        pem_wrap("PUBLIC KEY", &self.to_spki_der())
52    }
53
54    /// Encode the public key as the crate's flat XML form.
55    ///
56    /// This is a convenience export that mirrors the in-memory Rust fields
57    /// directly. Standards-based interchange should still prefer PKCS #1 or
58    /// SPKI.
59    #[must_use]
60    pub fn to_xml(&self) -> String {
61        xml_wrap(
62            "RsaPublicKey",
63            &[("e", self.exponent()), ("n", self.modulus())],
64        )
65    }
66
67    /// Decode a PKCS #1 `RSAPublicKey` structure from DER.
68    #[must_use]
69    pub fn from_pkcs1_der(der: &[u8]) -> Option<Self> {
70        let mut outer = DerReader::new(der);
71        let seq = outer.read_tlv(0x30)?;
72        if !outer.is_finished() {
73            return None;
74        }
75
76        let mut reader = DerReader::new(seq);
77        let modulus = reader.read_integer_biguint()?;
78        let public_exponent = reader.read_integer_biguint()?;
79        if !reader.is_finished() || public_exponent <= BigUint::one() {
80            return None;
81        }
82
83        Some(Self::from_components(public_exponent, modulus))
84    }
85
86    /// Decode `SubjectPublicKeyInfo` from DER.
87    #[must_use]
88    pub fn from_spki_der(der: &[u8]) -> Option<Self> {
89        let mut outer = DerReader::new(der);
90        let seq = outer.read_tlv(0x30)?;
91        if !outer.is_finished() {
92            return None;
93        }
94
95        let mut reader = DerReader::new(seq);
96        let alg_seq = reader.read_tlv(0x30)?;
97        let bit_string = reader.read_tlv(0x03)?;
98        if !reader.is_finished() || bit_string.is_empty() || bit_string[0] != 0 {
99            return None;
100        }
101
102        let mut alg_reader = DerReader::new(alg_seq);
103        let oid = alg_reader.read_tlv(0x06)?;
104        let _null = alg_reader.read_tlv(0x05)?;
105        if !alg_reader.is_finished() || oid != RSA_ENCRYPTION_OID {
106            return None;
107        }
108
109        Self::from_pkcs1_der(&bit_string[1..])
110    }
111
112    /// Decode a PKCS #1 `RSA PUBLIC KEY` PEM document.
113    #[must_use]
114    pub fn from_pkcs1_pem(pem: &str) -> Option<Self> {
115        let der = pem_unwrap("RSA PUBLIC KEY", pem)?;
116        Self::from_pkcs1_der(&der)
117    }
118
119    /// Decode a `PUBLIC KEY` PEM document (`SubjectPublicKeyInfo`).
120    #[must_use]
121    pub fn from_spki_pem(pem: &str) -> Option<Self> {
122        let der = pem_unwrap("PUBLIC KEY", pem)?;
123        Self::from_spki_der(&der)
124    }
125
126    /// Decode the public key from the crate's flat XML form.
127    #[must_use]
128    pub fn from_xml(xml: &str) -> Option<Self> {
129        let mut fields = xml_unwrap("RsaPublicKey", &["e", "n"], xml)?.into_iter();
130        let public_exponent = fields.next()?;
131        let modulus = fields.next()?;
132        if fields.next().is_some() || public_exponent <= BigUint::one() || modulus <= BigUint::one()
133        {
134            return None;
135        }
136        Some(Self::from_components(public_exponent, modulus))
137    }
138}
139
140impl RsaPrivateKey {
141    /// Encode the private key as the PKCS #1 `RSAPrivateKey` structure in DER.
142    ///
143    /// # Panics
144    ///
145    /// Panics only if the cached CRT values are internally inconsistent.
146    #[must_use]
147    pub fn to_pkcs1_der(&self) -> Vec<u8> {
148        let mut body = Vec::new();
149        body.extend(der_integer_u8(0));
150        body.extend(der_integer_biguint(self.modulus()));
151        body.extend(der_integer_biguint(self.public_exponent()));
152        body.extend(der_integer_biguint(self.exponent()));
153        body.extend(der_integer_biguint(self.prime1()));
154        body.extend(der_integer_biguint(self.prime2()));
155        body.extend(der_integer_biguint(self.crt_exponent1()));
156        body.extend(der_integer_biguint(self.crt_exponent2()));
157        body.extend(der_integer_biguint(self.crt_coefficient()));
158        der_sequence(&body)
159    }
160
161    /// Encode the private key as `PrivateKeyInfo` (`PKCS #8`) in DER.
162    #[must_use]
163    pub fn to_pkcs8_der(&self) -> Vec<u8> {
164        let pkcs1 = self.to_pkcs1_der();
165
166        let mut alg = Vec::new();
167        alg.extend(der_oid(RSA_ENCRYPTION_OID));
168        alg.extend(der_null());
169
170        let mut body = Vec::new();
171        body.extend(der_integer_u8(0));
172        body.extend(der_sequence(&alg));
173        body.extend(der_octet_string(&pkcs1));
174        der_sequence(&body)
175    }
176
177    /// Encode the private key as PKCS #1 `RSA PRIVATE KEY` PEM.
178    #[must_use]
179    pub fn to_pkcs1_pem(&self) -> String {
180        pem_wrap("RSA PRIVATE KEY", &self.to_pkcs1_der())
181    }
182
183    /// Encode the private key as `PRIVATE KEY` PEM (`PKCS #8`).
184    #[must_use]
185    pub fn to_pkcs8_pem(&self) -> String {
186        pem_wrap("PRIVATE KEY", &self.to_pkcs8_der())
187    }
188
189    /// Encode the private key as the crate's flat XML form.
190    ///
191    /// The XML form mirrors the stored key fields directly. PKCS #1 / PKCS #8
192    /// remain the preferred interoperable formats.
193    #[must_use]
194    pub fn to_xml(&self) -> String {
195        xml_wrap(
196            "RsaPrivateKey",
197            &[
198                ("e", self.public_exponent()),
199                ("d", self.exponent()),
200                ("n", self.modulus()),
201                ("p", self.prime1()),
202                ("q", self.prime2()),
203            ],
204        )
205    }
206
207    /// Decode a PKCS #1 `RSAPrivateKey` structure from DER.
208    #[must_use]
209    pub fn from_pkcs1_der(der: &[u8]) -> Option<Self> {
210        let mut outer = DerReader::new(der);
211        let seq = outer.read_tlv(0x30)?;
212        if !outer.is_finished() {
213            return None;
214        }
215
216        let mut reader = DerReader::new(seq);
217        let version = reader.read_integer_small()?;
218        if version != 0 {
219            return None;
220        }
221
222        let modulus = reader.read_integer_biguint()?;
223        let public_exponent = reader.read_integer_biguint()?;
224        let private_exponent = reader.read_integer_biguint()?;
225        let prime1 = reader.read_integer_biguint()?;
226        let prime2 = reader.read_integer_biguint()?;
227        let exponent1 = reader.read_integer_biguint()?;
228        let exponent2 = reader.read_integer_biguint()?;
229        let coefficient = reader.read_integer_biguint()?;
230        if !reader.is_finished() {
231            return None;
232        }
233
234        let (public, private) = Rsa::from_primes_with_exponent(&prime1, &prime2, &public_exponent)?;
235        if public.modulus() != &modulus || private.exponent() != &private_exponent {
236            return None;
237        }
238
239        if exponent1 != *private.crt_exponent1() || exponent2 != *private.crt_exponent2() {
240            return None;
241        }
242        if coefficient != *private.crt_coefficient() {
243            return None;
244        }
245
246        Some(private)
247    }
248
249    /// Decode `PrivateKeyInfo` (`PKCS #8`) from DER.
250    #[must_use]
251    pub fn from_pkcs8_der(der: &[u8]) -> Option<Self> {
252        let mut outer = DerReader::new(der);
253        let seq = outer.read_tlv(0x30)?;
254        if !outer.is_finished() {
255            return None;
256        }
257
258        let mut reader = DerReader::new(seq);
259        let version = reader.read_integer_small()?;
260        if version != 0 {
261            return None;
262        }
263
264        let alg_seq = reader.read_tlv(0x30)?;
265        let inner = reader.read_tlv(0x04)?;
266        if !reader.is_finished() {
267            return None;
268        }
269
270        let mut alg_reader = DerReader::new(alg_seq);
271        let oid = alg_reader.read_tlv(0x06)?;
272        let _null = alg_reader.read_tlv(0x05)?;
273        if !alg_reader.is_finished() || oid != RSA_ENCRYPTION_OID {
274            return None;
275        }
276
277        Self::from_pkcs1_der(inner)
278    }
279
280    /// Decode a PKCS #1 `RSA PRIVATE KEY` PEM document.
281    #[must_use]
282    pub fn from_pkcs1_pem(pem: &str) -> Option<Self> {
283        let der = pem_unwrap("RSA PRIVATE KEY", pem)?;
284        Self::from_pkcs1_der(&der)
285    }
286
287    /// Decode a `PRIVATE KEY` PEM document (`PKCS #8`).
288    #[must_use]
289    pub fn from_pkcs8_pem(pem: &str) -> Option<Self> {
290        let der = pem_unwrap("PRIVATE KEY", pem)?;
291        Self::from_pkcs8_der(&der)
292    }
293
294    /// Decode the private key from the crate's flat XML form.
295    #[must_use]
296    pub fn from_xml(xml: &str) -> Option<Self> {
297        let mut fields = xml_unwrap("RsaPrivateKey", &["e", "d", "n", "p", "q"], xml)?.into_iter();
298        let public_exponent = fields.next()?;
299        let private_exponent = fields.next()?;
300        let modulus = fields.next()?;
301        let prime1 = fields.next()?;
302        let prime2 = fields.next()?;
303        if fields.next().is_some() {
304            return None;
305        }
306
307        let (public, private) = Rsa::from_primes_with_exponent(&prime1, &prime2, &public_exponent)?;
308        if public.modulus() != &modulus || private.exponent() != &private_exponent {
309            return None;
310        }
311        Some(private)
312    }
313}
314
315fn der_tlv(tag: u8, content: &[u8]) -> Vec<u8> {
316    let mut out = Vec::with_capacity(2 + content.len() + 5);
317    out.push(tag);
318    out.extend(der_len(content.len()));
319    out.extend_from_slice(content);
320    out
321}
322
323fn der_len(len: usize) -> Vec<u8> {
324    if len < 128 {
325        // DER short-form length: the length octet is the value itself.
326        return vec![u8::try_from(len).expect("short DER length fits in u8")];
327    }
328
329    let bytes = len.to_be_bytes();
330    let first_nonzero = bytes
331        .iter()
332        .position(|&byte| byte != 0)
333        .expect("non-zero long DER length has a significant byte");
334    let len_bytes = &bytes[first_nonzero..];
335
336    let mut out = Vec::with_capacity(1 + len_bytes.len());
337    out.push(0x80 | u8::try_from(len_bytes.len()).expect("usize length-of-length fits in u8"));
338    out.extend_from_slice(len_bytes);
339    out
340}
341
342fn der_sequence(content: &[u8]) -> Vec<u8> {
343    der_tlv(0x30, content)
344}
345
346fn der_octet_string(content: &[u8]) -> Vec<u8> {
347    der_tlv(0x04, content)
348}
349
350fn der_bit_string(content: &[u8]) -> Vec<u8> {
351    let mut body = Vec::with_capacity(1 + content.len());
352    // DER BIT STRING prefixes the payload with the number of unused bits in
353    // the final octet. Zero means the payload ends on a byte boundary.
354    body.push(0);
355    body.extend_from_slice(content);
356    der_tlv(0x03, &body)
357}
358
359fn der_null() -> Vec<u8> {
360    der_tlv(0x05, &[])
361}
362
363fn der_oid(content: &[u8]) -> Vec<u8> {
364    der_tlv(0x06, content)
365}
366
367fn der_integer_u8(value: u8) -> Vec<u8> {
368    der_integer_bytes(&[value])
369}
370
371fn der_integer_biguint(value: &BigUint) -> Vec<u8> {
372    let bytes = value.to_be_bytes();
373    der_integer_bytes(&bytes)
374}
375
376fn der_integer_bytes(bytes: &[u8]) -> Vec<u8> {
377    let mut body = if let Some(first_nonzero) = bytes.iter().position(|&byte| byte != 0) {
378        bytes[first_nonzero..].to_vec()
379    } else {
380        vec![0]
381    };
382
383    // DER INTEGER is signed two's-complement, so prepend a zero byte when the
384    // high bit would otherwise mark the value as negative.
385    if body[0] & 0x80 != 0 {
386        body.insert(0, 0);
387    }
388
389    der_tlv(0x02, &body)
390}
391
392struct DerReader<'a> {
393    data: &'a [u8],
394    pos: usize,
395}
396
397impl<'a> DerReader<'a> {
398    fn new(data: &'a [u8]) -> Self {
399        Self { data, pos: 0 }
400    }
401
402    fn is_finished(&self) -> bool {
403        self.pos == self.data.len()
404    }
405
406    fn read_tlv(&mut self, expected_tag: u8) -> Option<&'a [u8]> {
407        let tag = *self.data.get(self.pos)?;
408        self.pos += 1;
409        if tag != expected_tag {
410            return None;
411        }
412
413        let first = *self.data.get(self.pos)?;
414        self.pos += 1;
415        let len = if first & 0x80 == 0 {
416            // DER short-form length.
417            usize::from(first)
418        } else {
419            // DER long-form length: the low seven bits say how many following
420            // octets encode the content length in big-endian order.
421            let count = usize::from(first & 0x7f);
422            if count == 0 || count > core::mem::size_of::<usize>() {
423                return None;
424            }
425            let mut len = 0usize;
426            for _ in 0..count {
427                len = (len << 8) | usize::from(*self.data.get(self.pos)?);
428                self.pos += 1;
429            }
430            len
431        };
432
433        let end = self.pos.checked_add(len)?;
434        let content = self.data.get(self.pos..end)?;
435        self.pos = end;
436        Some(content)
437    }
438
439    fn read_integer_biguint(&mut self) -> Option<BigUint> {
440        let content = self.read_tlv(0x02)?;
441        if content.is_empty() {
442            return None;
443        }
444        // Negative DER INTEGER encodings are not valid for RSA key material.
445        if content[0] & 0x80 != 0 {
446            return None;
447        }
448
449        let body = if content.len() > 1 && content[0] == 0 {
450            // Positive DER INTEGERs may carry a sign-extension zero byte when
451            // the true high bit is set; strip that back off for BigUint.
452            &content[1..]
453        } else {
454            content
455        };
456        Some(BigUint::from_be_bytes(body))
457    }
458
459    fn read_integer_small(&mut self) -> Option<u8> {
460        let value = self.read_integer_biguint()?;
461        let bytes = value.to_be_bytes();
462        if bytes.len() != 1 {
463            return None;
464        }
465        Some(bytes[0])
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use super::{RsaPrivateKey, RsaPublicKey};
472    use crate::public_key::rsa::Rsa;
473    use crate::vt::BigUint;
474
475    #[test]
476    fn spki_roundtrip() {
477        let p = BigUint::from_u64(61);
478        let q = BigUint::from_u64(53);
479        let (public, _) = Rsa::from_primes(&p, &q).expect("valid RSA key");
480
481        let der = public.to_spki_der();
482        let parsed = RsaPublicKey::from_spki_der(&der).expect("parse SPKI");
483        assert_eq!(parsed, public);
484
485        let pem = public.to_spki_pem();
486        let parsed = RsaPublicKey::from_spki_pem(&pem).expect("parse SPKI PEM");
487        assert_eq!(parsed, public);
488    }
489
490    #[test]
491    fn pkcs8_roundtrip() {
492        let p = BigUint::from_u64(61);
493        let q = BigUint::from_u64(53);
494        let (_, private) = Rsa::from_primes(&p, &q).expect("valid RSA key");
495
496        let der = private.to_pkcs8_der();
497        let parsed = RsaPrivateKey::from_pkcs8_der(&der).expect("parse PKCS#8");
498        assert_eq!(parsed, private);
499
500        let pem = private.to_pkcs8_pem();
501        let parsed = RsaPrivateKey::from_pkcs8_pem(&pem).expect("parse PKCS#8 PEM");
502        assert_eq!(parsed, private);
503    }
504
505    #[test]
506    fn xml_roundtrip() {
507        let p = BigUint::from_u64(61);
508        let q = BigUint::from_u64(53);
509        let (public, private) = Rsa::from_primes(&p, &q).expect("valid RSA key");
510
511        let public_xml = public.to_xml();
512        let private_xml = private.to_xml();
513        assert_eq!(RsaPublicKey::from_xml(&public_xml), Some(public));
514        assert_eq!(RsaPrivateKey::from_xml(&private_xml), Some(private));
515    }
516
517    #[test]
518    fn generated_key_xml_roundtrip() {
519        let mut drbg = crate::CtrDrbgAes256::new(&[0xc1; 48]);
520        let (public, private) = Rsa::generate(&mut drbg, 64).expect("generated RSA key");
521
522        let public_der = public.to_spki_der();
523        let public_xml = public.to_xml();
524        let private_xml = private.to_xml();
525
526        assert_eq!(
527            RsaPublicKey::from_spki_der(&public_der),
528            Some(public.clone())
529        );
530        assert_eq!(RsaPublicKey::from_xml(&public_xml), Some(public));
531        assert_eq!(RsaPrivateKey::from_xml(&private_xml), Some(private));
532    }
533
534    #[test]
535    fn openssl_accepts_spki_pem() {
536        let p = BigUint::from_u64(61);
537        let q = BigUint::from_u64(53);
538        let (public, _) = Rsa::from_primes(&p, &q).expect("valid RSA key");
539
540        let Some(expected) = crate::test_utils::run_openssl(
541            &[
542                "pkey", "-pubin", "-inform", "PEM", "-pubout", "-outform", "DER",
543            ],
544            public.to_spki_pem().as_bytes(),
545        ) else {
546            return;
547        };
548
549        assert_eq!(expected, public.to_spki_der());
550    }
551
552    #[test]
553    fn openssl_accepts_pkcs8_pem() {
554        let p = BigUint::from_u64(61);
555        let q = BigUint::from_u64(53);
556        let (_, private) = Rsa::from_primes(&p, &q).expect("valid RSA key");
557
558        let Some(expected) = crate::test_utils::run_openssl(
559            &["pkey", "-inform", "PEM", "-outform", "DER"],
560            private.to_pkcs8_pem().as_bytes(),
561        ) else {
562            return;
563        };
564
565        assert_eq!(expected, private.to_pkcs8_der());
566    }
567}