Skip to main content

rite_sdk/
key_material.rs

1//! Public key and certificate byte strings, with their encoding in the type.
2//!
3//! Key material reaches a ceremony as bytes, and the same `Vec<u8>` could hold
4//! an SPKI public key, a PEM armouring of one, or a whole X.509 certificate.
5//! [`PublicKeyDer`] and [`CertificateDer`] separate those, so a certificate
6//! cannot be handed to something expecting a key and fail deep inside an ASN.1
7//! parser.
8//!
9//! Both validate their structure at construction. Neither checks the algorithm
10//! is one Rite supports: a key Rite cannot verify under is still a well-formed
11//! key, and rejecting it here would put every future algorithm behind an SDK
12//! release.
13//!
14//! [`CertificateDer::public_key`] is the only conversion between the two. It is
15//! a method rather than a `From` impl because reading a signer's key out of
16//! their certificate is a decision the caller makes, not a coercion.
17
18use serde::{Deserialize, Serialize};
19use x509_cert::Certificate;
20use x509_cert::der::asn1::UintRef;
21use x509_cert::der::oid::db::fips204::{ID_ML_DSA_44, ID_ML_DSA_65, ID_ML_DSA_87};
22use x509_cert::der::oid::db::rfc5912::{
23    ID_EC_PUBLIC_KEY, RSA_ENCRYPTION, SECP_256_R_1, SECP_384_R_1,
24};
25use x509_cert::der::oid::db::rfc8410::ID_ED_25519;
26use x509_cert::der::{Decode, Encode, Reader, SliceReader};
27use x509_cert::spki::SubjectPublicKeyInfoRef;
28
29use crate::backend::BackendError;
30use crate::types::KeyAlgorithm;
31
32/// A public key in `SubjectPublicKeyInfo` (SPKI) DER form.
33///
34/// The one encoding a public key travels in inside Rite. PEM belongs at the
35/// edges, where a key is read from a file or written to one.
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(try_from = "Vec<u8>", into = "Vec<u8>")]
38pub struct PublicKeyDer(Vec<u8>);
39
40impl PublicKeyDer {
41    /// Wrap SPKI DER bytes after checking they parse.
42    ///
43    /// Takes ownership because callers hold the bytes a backend or a parser
44    /// just produced, and the type keeps them.
45    ///
46    /// # Errors
47    ///
48    /// Returns [`BackendError::InvalidKeyFormat`] when the bytes are not a
49    /// `SubjectPublicKeyInfo`, which is what a certificate or a PEM armouring
50    /// arriving here looks like.
51    pub fn new(der: Vec<u8>) -> Result<Self, BackendError> {
52        SubjectPublicKeyInfoRef::from_der(&der).map_err(|e| {
53            BackendError::InvalidKeyFormat(format!("not a SubjectPublicKeyInfo: {e}"))
54        })?;
55        Ok(Self(der))
56    }
57
58    /// Read a public key out of whatever a ceremony hands over.
59    ///
60    /// Accepts the four shapes a signer's key arrives in: SPKI DER, SPKI PEM,
61    /// certificate DER, and certificate PEM. A key loaded from a file is PEM
62    /// more often than not, and a counterparty usually sends a certificate
63    /// rather than a bare key, so refusing either would push the conversion
64    /// onto the ceremony author.
65    ///
66    /// Use [`PublicKeyDer::new`] where the encoding is already known; this is
67    /// for the boundary where it is not.
68    ///
69    /// # Errors
70    ///
71    /// Returns [`BackendError::InvalidKeyFormat`] when the bytes are none of
72    /// the four, naming what was accepted.
73    pub fn from_key_material(bytes: &[u8]) -> Result<Self, BackendError> {
74        // PEM says what it holds, so the label decides and a wrong one is worth
75        // reporting: a private key armoured here would otherwise come back as
76        // unreadable bytes.
77        if let Ok((label, der)) = x509_cert::der::pem::decode_vec(bytes) {
78            return match label {
79                "PUBLIC KEY" => Self::new(der),
80                "CERTIFICATE" => certificate_public_key(&der),
81                other => Err(BackendError::InvalidKeyFormat(format!(
82                    "PEM block is labelled '{other}', expected 'PUBLIC KEY' or 'CERTIFICATE'"
83                ))),
84            };
85        }
86
87        // DER says nothing about itself, so try both shapes.
88        if let Ok(key) = Self::new(bytes.to_vec()) {
89            return Ok(key);
90        }
91        // Neither shape parsed. The certificate attempt carries the more useful
92        // reason, since bytes that reach here and are close to valid are far
93        // more often a certificate than a bare key.
94        certificate_public_key(bytes).map_err(|e| {
95            BackendError::InvalidKeyFormat(format!(
96                "not a public key or certificate, in DER or PEM ({e})"
97            ))
98        })
99    }
100
101    /// The SPKI DER bytes.
102    #[must_use]
103    pub fn as_bytes(&self) -> &[u8] {
104        &self.0
105    }
106
107    /// The key algorithm, read from the key's own structure.
108    ///
109    /// Key bytes carry no ceremony metadata, so a caller that must know what it
110    /// is holding, to pick a signature algorithm for instance, recovers it from
111    /// the `AlgorithmIdentifier`. RSA is the one case the identifier does not
112    /// settle: it names rsaEncryption without a size, so the modulus decides.
113    ///
114    /// # Errors
115    ///
116    /// Returns [`BackendError::UnsupportedAlgorithm`] for a key type or size
117    /// Rite has no [`KeyAlgorithm`] for, and [`BackendError::InvalidKeyFormat`]
118    /// when an RSA key's modulus cannot be read.
119    pub fn algorithm(&self) -> Result<KeyAlgorithm, BackendError> {
120        // Every constructor parses, so this cannot fail. It is written as a
121        // fallible parse rather than an unwrap because the method returns
122        // `Result` for unsupported algorithms anyway.
123        let spki = SubjectPublicKeyInfoRef::from_der(&self.0)
124            .map_err(|e| BackendError::InvalidKeyFormat(format!("SPKI is unreadable: {e}")))?;
125
126        match spki.algorithm.oid {
127            RSA_ENCRYPTION => {
128                let key = spki.subject_public_key.as_bytes().ok_or_else(|| {
129                    BackendError::InvalidKeyFormat(
130                        "RSA public key is not a whole number of bytes".to_string(),
131                    )
132                })?;
133                rsa_algorithm(key)
134            }
135            ID_EC_PUBLIC_KEY => match spki.algorithm.parameters_oid() {
136                Ok(SECP_256_R_1) => Ok(KeyAlgorithm::EcdsaP256),
137                Ok(SECP_384_R_1) => Ok(KeyAlgorithm::EcdsaP384),
138                Ok(curve) => Err(BackendError::UnsupportedAlgorithm(format!(
139                    "EC curve {curve} is not supported (expected P-256 or P-384)"
140                ))),
141                Err(e) => Err(BackendError::InvalidKeyFormat(format!(
142                    "EC key does not name a curve: {e}"
143                ))),
144            },
145            ID_ED_25519 => Ok(KeyAlgorithm::Ed25519),
146            ID_ML_DSA_44 => Ok(KeyAlgorithm::MlDsa44),
147            ID_ML_DSA_65 => Ok(KeyAlgorithm::MlDsa65),
148            ID_ML_DSA_87 => Ok(KeyAlgorithm::MlDsa87),
149            oid => Err(BackendError::UnsupportedAlgorithm(format!(
150                "public key algorithm {oid} is not supported"
151            ))),
152        }
153    }
154}
155
156impl From<PublicKeyDer> for Vec<u8> {
157    fn from(key: PublicKeyDer) -> Self {
158        key.0
159    }
160}
161
162impl TryFrom<Vec<u8>> for PublicKeyDer {
163    type Error = BackendError;
164
165    fn try_from(der: Vec<u8>) -> Result<Self, Self::Error> {
166        Self::new(der)
167    }
168}
169
170/// An X.509 certificate in DER form.
171#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
172#[serde(try_from = "Vec<u8>", into = "Vec<u8>")]
173pub struct CertificateDer(Vec<u8>);
174
175impl CertificateDer {
176    /// Wrap certificate DER bytes after checking they parse.
177    ///
178    /// # Errors
179    ///
180    /// Returns [`BackendError::InvalidKeyFormat`] when the bytes are not an
181    /// X.509 certificate.
182    pub fn new(der: Vec<u8>) -> Result<Self, BackendError> {
183        Certificate::from_der(&der).map_err(|e| {
184            BackendError::InvalidKeyFormat(format!("not an X.509 certificate: {e}"))
185        })?;
186        Ok(Self(der))
187    }
188
189    /// Read a certificate out of whatever a ceremony hands over.
190    ///
191    /// Accepts certificate DER and certificate PEM. Certificates reach a
192    /// ceremony from files and counterparties, where PEM is the common
193    /// encoding, so the sniff belongs here rather than at each call site.
194    ///
195    /// The counterpart to [`PublicKeyDer::from_key_material`]. Both exist so
196    /// that deciding what an encoding is happens in one place per type.
197    ///
198    /// # Errors
199    ///
200    /// Returns [`BackendError::InvalidKeyFormat`] when the bytes are neither,
201    /// or when a PEM block names something other than a certificate.
202    pub fn from_key_material(bytes: &[u8]) -> Result<Self, BackendError> {
203        if let Ok((label, der)) = x509_cert::der::pem::decode_vec(bytes) {
204            return match label {
205                "CERTIFICATE" => Self::new(der),
206                other => Err(BackendError::InvalidKeyFormat(format!(
207                    "PEM block is labelled '{other}', expected 'CERTIFICATE'"
208                ))),
209            };
210        }
211        Self::new(bytes.to_vec())
212    }
213
214    /// The certificate DER bytes.
215    #[must_use]
216    pub fn as_bytes(&self) -> &[u8] {
217        &self.0
218    }
219
220    /// The subject public key, as SPKI DER.
221    ///
222    /// A signer's public key usually reaches a ceremony inside a certificate
223    /// rather than on its own: `piv_read_certificate` pulls one off a card, and
224    /// a counterparty sends one. This lets a step name the certificate instead
225    /// of the author extracting the key first.
226    ///
227    /// # Errors
228    ///
229    /// Returns [`BackendError::InvalidKeyFormat`] when the certificate or its
230    /// subject public key cannot be re-encoded.
231    pub fn public_key(&self) -> Result<PublicKeyDer, BackendError> {
232        certificate_public_key(&self.0)
233    }
234}
235
236/// Read the subject public key out of certificate DER.
237///
238/// Takes bytes rather than a [`CertificateDer`] so a caller that only wants the
239/// key does not have to build, and validate, a certificate wrapper it will drop
240/// on the next line.
241fn certificate_public_key(der: &[u8]) -> Result<PublicKeyDer, BackendError> {
242    let certificate = Certificate::from_der(der)
243        .map_err(|e| BackendError::InvalidKeyFormat(format!("not an X.509 certificate: {e}")))?;
244    let spki = certificate
245        .tbs_certificate()
246        .subject_public_key_info()
247        .to_der()
248        .map_err(|e| {
249            BackendError::InvalidKeyFormat(format!(
250                "certificate's public key cannot be encoded: {e}"
251            ))
252        })?;
253    PublicKeyDer::new(spki)
254}
255
256impl From<CertificateDer> for Vec<u8> {
257    fn from(certificate: CertificateDer) -> Self {
258        certificate.0
259    }
260}
261
262impl TryFrom<Vec<u8>> for CertificateDer {
263    type Error = BackendError;
264
265    fn try_from(der: Vec<u8>) -> Result<Self, Self::Error> {
266        Self::new(der)
267    }
268}
269
270/// Classify an RSA key by its modulus size.
271///
272/// `key` is the `subjectPublicKey` payload, an `RSAPublicKey` sequence of
273/// modulus and public exponent (RFC 8017 appendix A.1.1).
274fn rsa_algorithm(key: &[u8]) -> Result<KeyAlgorithm, BackendError> {
275    let mut reader = SliceReader::new(key).map_err(|e| {
276        BackendError::InvalidKeyFormat(format!("RSA public key is unreadable: {e}"))
277    })?;
278    let modulus_bits = reader
279        .sequence(|body| {
280            let modulus: UintRef<'_> = body.decode()?;
281            Ok::<_, x509_cert::der::Error>(bit_length(modulus.as_bytes()))
282        })
283        .map_err(|e| BackendError::InvalidKeyFormat(format!("RSA modulus cannot be read: {e}")))?;
284
285    match modulus_bits {
286        2048 => Ok(KeyAlgorithm::Rsa2048),
287        4096 => Ok(KeyAlgorithm::Rsa4096),
288        bits => Err(BackendError::UnsupportedAlgorithm(format!(
289            "RSA key size {bits} bits not supported (expected 2048 or 4096)"
290        ))),
291    }
292}
293
294/// Bit length of a big-endian integer with leading zero bytes already stripped.
295///
296/// Saturates rather than wrapping. A modulus long enough to overflow is not a
297/// supported key size either way, so it reaches the same rejection.
298fn bit_length(magnitude: &[u8]) -> u32 {
299    let Some((&top, rest)) = magnitude.split_first() else {
300        return 0;
301    };
302
303    u32::try_from(rest.len())
304        .unwrap_or(u32::MAX)
305        .saturating_mul(8)
306        .saturating_add(u8::BITS.saturating_sub(top.leading_zeros()))
307}
308
309#[cfg(test)]
310mod tests {
311    use super::*;
312
313    /// SPKI DER for one key per algorithm, produced with OpenSSL and pinned as
314    /// bytes. Generating them at test time would let a change in Rite's own key
315    /// generation move the expected answer along with the input.
316    ///
317    /// Reproduce with, for each algorithm:
318    /// `openssl genpkey -algorithm <alg> | openssl pkey -pubout -outform DER`
319    mod vectors {
320        pub const RSA_2048: &[u8] = include_bytes!("../tests/vectors/rsa2048.spki.der");
321        pub const RSA_4096: &[u8] = include_bytes!("../tests/vectors/rsa4096.spki.der");
322        pub const P256: &[u8] = include_bytes!("../tests/vectors/p256.spki.der");
323        pub const P384: &[u8] = include_bytes!("../tests/vectors/p384.spki.der");
324        pub const ED25519: &[u8] = include_bytes!("../tests/vectors/ed25519.spki.der");
325        pub const ML_DSA_44: &[u8] = include_bytes!("../tests/vectors/mldsa44.spki.der");
326        pub const ML_DSA_65: &[u8] = include_bytes!("../tests/vectors/mldsa65.spki.der");
327        pub const ML_DSA_87: &[u8] = include_bytes!("../tests/vectors/mldsa87.spki.der");
328        pub const CERTIFICATE: &[u8] = include_bytes!("../tests/vectors/p256.cert.der");
329    }
330
331    #[test]
332    fn reads_the_algorithm_out_of_each_key_type() {
333        let cases = [
334            (vectors::RSA_2048, KeyAlgorithm::Rsa2048),
335            (vectors::RSA_4096, KeyAlgorithm::Rsa4096),
336            (vectors::P256, KeyAlgorithm::EcdsaP256),
337            (vectors::P384, KeyAlgorithm::EcdsaP384),
338            (vectors::ED25519, KeyAlgorithm::Ed25519),
339            (vectors::ML_DSA_44, KeyAlgorithm::MlDsa44),
340            (vectors::ML_DSA_65, KeyAlgorithm::MlDsa65),
341            (vectors::ML_DSA_87, KeyAlgorithm::MlDsa87),
342        ];
343
344        for (der, expected) in cases {
345            let key = PublicKeyDer::new(der.to_vec()).expect("vector is valid SPKI");
346            assert_eq!(key.algorithm().unwrap(), expected);
347        }
348    }
349
350    /// The mistake the type exists to prevent. Certificate DER used to reach an
351    /// SPKI parser and fail with an ASN.1 tag error from inside the provider.
352    #[test]
353    fn rejects_a_certificate_offered_as_a_public_key() {
354        let err = PublicKeyDer::new(vectors::CERTIFICATE.to_vec()).unwrap_err();
355        assert!(
356            err.to_string().contains("not a SubjectPublicKeyInfo"),
357            "{err}"
358        );
359    }
360
361    #[test]
362    fn reads_the_public_key_out_of_a_certificate() {
363        let certificate =
364            CertificateDer::new(vectors::CERTIFICATE.to_vec()).expect("vector is a certificate");
365        let key = certificate.public_key().unwrap();
366        assert_eq!(key.algorithm().unwrap(), KeyAlgorithm::EcdsaP256);
367        assert_eq!(key.as_bytes(), vectors::P256);
368    }
369
370    #[test]
371    fn rejects_a_public_key_offered_as_a_certificate() {
372        assert!(CertificateDer::new(vectors::P256.to_vec()).is_err());
373    }
374
375    /// Trailing bytes after a valid key are a truncation or splice, not a key.
376    #[test]
377    fn rejects_trailing_data() {
378        let mut der = vectors::P256.to_vec();
379        der.push(0);
380        assert!(PublicKeyDer::new(der).is_err());
381    }
382
383    #[test]
384    fn rejects_empty_and_truncated_input() {
385        assert!(PublicKeyDer::new(Vec::new()).is_err());
386        let truncated = vectors::P256
387            .get(..10)
388            .expect("vector is longer than 10 bytes");
389        assert!(PublicKeyDer::new(truncated.to_vec()).is_err());
390    }
391
392    /// Serde round-trips through the raw bytes, and validates on the way in.
393    #[test]
394    fn deserializing_rejects_bytes_that_are_not_a_key() {
395        let key = PublicKeyDer::new(vectors::P256.to_vec()).unwrap();
396        let json = serde_json::to_string(&key).unwrap();
397        assert_eq!(serde_json::from_str::<PublicKeyDer>(&json).unwrap(), key);
398
399        let certificate_json = serde_json::to_string(vectors::CERTIFICATE).unwrap();
400        assert!(serde_json::from_str::<PublicKeyDer>(&certificate_json).is_err());
401    }
402
403    /// PEM armour a DER body under `label`, the way a file on disk carries it.
404    fn armour(label: &str, der: &[u8]) -> Vec<u8> {
405        x509_cert::der::pem::encode_string(label, x509_cert::der::pem::LineEnding::LF, der)
406            .expect("armouring is infallible for a valid label")
407            .into_bytes()
408    }
409
410    /// The four shapes a signer's key arrives in must all resolve to the key.
411    #[test]
412    fn reads_a_key_from_der_and_pem_and_certificates() {
413        let expected = PublicKeyDer::new(vectors::P256.to_vec()).unwrap();
414
415        let sources = [
416            vectors::P256.to_vec(),
417            armour("PUBLIC KEY", vectors::P256),
418            vectors::CERTIFICATE.to_vec(),
419            armour("CERTIFICATE", vectors::CERTIFICATE),
420        ];
421
422        for source in sources {
423            assert_eq!(PublicKeyDer::from_key_material(&source).unwrap(), expected);
424        }
425    }
426
427    /// A PEM block naming something else is an authoring mistake worth saying
428    /// out loud: a private key here would otherwise fail as unreadable bytes.
429    #[test]
430    fn refuses_a_pem_block_that_is_not_a_key_or_certificate() {
431        let private = armour("PRIVATE KEY", vectors::P256);
432        let err = PublicKeyDer::from_key_material(&private).unwrap_err();
433        assert!(err.to_string().contains("PRIVATE KEY"), "{err}");
434    }
435
436    #[test]
437    fn refuses_key_material_that_is_neither() {
438        let err = PublicKeyDer::from_key_material(b"just some bytes").unwrap_err();
439        assert!(
440            err.to_string().contains("public key or certificate"),
441            "{err}"
442        );
443    }
444
445    /// A certificate arrives in DER or PEM, and both must reach the same value.
446    #[test]
447    fn reads_a_certificate_from_der_and_pem() {
448        let expected = CertificateDer::new(vectors::CERTIFICATE.to_vec()).unwrap();
449
450        for source in [
451            vectors::CERTIFICATE.to_vec(),
452            armour("CERTIFICATE", vectors::CERTIFICATE),
453        ] {
454            assert_eq!(
455                CertificateDer::from_key_material(&source).unwrap(),
456                expected
457            );
458        }
459    }
460
461    #[test]
462    fn refuses_a_pem_block_that_is_not_a_certificate() {
463        let key = armour("PUBLIC KEY", vectors::P256);
464        let err = CertificateDer::from_key_material(&key).unwrap_err();
465        assert!(err.to_string().contains("PUBLIC KEY"), "{err}");
466    }
467
468    #[test]
469    fn bit_length_counts_from_the_top_set_bit() {
470        assert_eq!(bit_length(&[]), 0);
471        assert_eq!(bit_length(&[0x01]), 1);
472        assert_eq!(bit_length(&[0x80]), 8);
473        assert_eq!(bit_length(&[0x80, 0x00]), 16);
474        assert_eq!(bit_length(&[0x0f, 0xff]), 12);
475    }
476}