• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::collections::HashMap;
2 
3 use crate::cbor::field_value::FieldValue;
4 use crate::cbor::{canonicalize_map, serialize, value_from_bytes};
5 use crate::dice::ChainForm;
6 use crate::rkp::{Csr, CsrPayload, DeviceInfo, DeviceInfoVersion, KeysToSign, ProtectedData};
7 use crate::session::{RkpInstance, Session};
8 use anyhow::{anyhow, bail, ensure, Context, Result};
9 use base64::{prelude::BASE64_STANDARD, Engine};
10 use ciborium::value::Value;
11 use coset::{AsCborValue, CoseKey};
12 use openssl::pkey::Id;
13 use openssl::stack::Stack;
14 use openssl::x509::store::X509StoreBuilder;
15 use openssl::x509::verify::X509VerifyFlags;
16 use openssl::x509::{X509StoreContext, X509};
17 
18 const VERSION_OR_DEVICE_INFO_INDEX: usize = 0;
19 
20 impl KeysToSign {
from_bytes(buffer: &[u8]) -> Result<Self>21     pub(crate) fn from_bytes(buffer: &[u8]) -> Result<Self> {
22         let value = value_from_bytes(buffer)?;
23         let field_value = FieldValue::from_value("KeysToSign", value);
24         Self::from_value(field_value)
25     }
from_value(value: FieldValue) -> Result<KeysToSign>26     fn from_value(value: FieldValue) -> Result<KeysToSign> {
27         Ok(KeysToSign(
28             value.into_array()?.into_iter().map(|v| CoseKey::from_cbor_value(v).unwrap()).collect(),
29         ))
30     }
31 }
32 
33 impl CsrPayload {
from_value(value: &Value, session: &Session) -> Result<Self>34     fn from_value(value: &Value, session: &Session) -> Result<Self> {
35         let serialized = match value.clone().into_bytes() {
36             Ok(bytes) => bytes,
37             Err(_) => bail!("CsrPayload had no bytes"),
38         };
39 
40         let mut csr_payload = match value_from_bytes(serialized.as_slice())? {
41             Value::Array(a) => a,
42             other => bail!("CsrPayload is expected to be an array, found {other:?}"),
43         };
44 
45         let keys_to_sign = FieldValue::from_optional_value("KeysToSign", csr_payload.pop());
46         let device_info = FieldValue::from_optional_value("DeviceInfo", csr_payload.pop());
47         let certificate_type =
48             FieldValue::from_optional_value("CertificateType", csr_payload.pop());
49         let version = FieldValue::from_optional_value("Version", csr_payload.pop()).into_u64()?;
50         if version != 3 {
51             bail!("Invalid CsrPayload version. Only '3' is supported");
52         }
53 
54         let certificate_type = certificate_type.into_string()?;
55 
56         const CERTIFICATE_TYPE_RKPVM: &str = "rkp-vm";
57         match session.options.rkp_instance {
58             RkpInstance::Avf => ensure!(
59                 CERTIFICATE_TYPE_RKPVM == certificate_type,
60                 "CertificateType must be 'rkp-vm' for AVF"
61             ),
62             _ => ensure!(
63                 CERTIFICATE_TYPE_RKPVM != certificate_type,
64                 "CertificateType must not be 'rkp-vm' for non-AVF"
65             ),
66         }
67 
68         let device_info = DeviceInfo::from_cbor_values(
69             device_info.into_map()?,
70             Some(DeviceInfoVersion::V3),
71             session.options.is_factory,
72         )?;
73         let keys_to_sign = KeysToSign::from_value(keys_to_sign)?;
74 
75         Ok(CsrPayload { serialized, certificate_type, device_info, keys_to_sign })
76     }
77 }
78 
79 impl Csr {
80     /// Parse base64-encoded CBOR data as a Certificate Signing Request.
from_base64_cbor<S: AsRef<[u8]>>(session: &Session, base64: &S) -> Result<Self>81     pub fn from_base64_cbor<S: AsRef<[u8]>>(session: &Session, base64: &S) -> Result<Self> {
82         let cbor: Vec<u8> = BASE64_STANDARD.decode(base64).context("invalid base64 CSR")?;
83         Self::from_cbor(session, cbor.as_slice())
84     }
85 
86     /// Read and parse CBOR data as a Certificate Signing Request.
from_cbor<S: std::io::Read>(session: &Session, cbor: S) -> Result<Self>87     pub fn from_cbor<S: std::io::Read>(session: &Session, cbor: S) -> Result<Self> {
88         let value: Value = ciborium::de::from_reader(cbor).context("invalid CBOR")?;
89         let mut array = match value {
90             Value::Array(a) if a.is_empty() => bail!("CSR CBOR is an empty array"),
91             Value::Array(a) => a,
92             other => bail!("expected array, found {other:?}"),
93         };
94         let version_or_device_info =
95             std::mem::replace(&mut array[VERSION_OR_DEVICE_INFO_INDEX], Value::Null);
96         match version_or_device_info {
97             Value::Array(device_info) => Self::v2_from_cbor_values(session, array, device_info),
98             Value::Integer(i) => Self::v3_from_authenticated_request(session, array, i.into()),
99             other => Err(anyhow!(
100                 "Expected integer or array at index {VERSION_OR_DEVICE_INFO_INDEX}, \
101                 found {other:?}"
102             )),
103         }
104     }
105 
v2_from_cbor_values( session: &Session, mut csr: Vec<Value>, mut device_info: Vec<Value>, ) -> Result<Self>106     fn v2_from_cbor_values(
107         session: &Session,
108         mut csr: Vec<Value>,
109         mut device_info: Vec<Value>,
110     ) -> Result<Self> {
111         let maced_keys_to_sign =
112             FieldValue::from_optional_value("MacedKeysToSign", csr.pop()).into_cose_mac0()?;
113         let encrypted_protected_data =
114             FieldValue::from_optional_value("ProtectedData", csr.pop()).into_cose_encrypt()?;
115         let challenge = FieldValue::from_optional_value("Challenge", csr.pop()).into_bytes()?;
116 
117         ensure!(device_info.len() == 2, "Device info should contain exactly 2 entries");
118         device_info.pop(); // ignore unverified info
119         let device_info = device_info.pop().unwrap();
120         let device_info_serialized = serialize(device_info.clone());
121         let device_info_canonicalized = canonicalize_map(device_info.clone())?;
122         if device_info_canonicalized != device_info_serialized {
123             match session.options.verbose {
124                 true => bail!(
125                     "Device info is not canonical:\nexpected: {:?}\nactual: {:?}",
126                     &hex::encode(device_info_canonicalized),
127                     &hex::encode(device_info_serialized)
128                 ),
129                 false => bail!("Device info is not canonical"),
130             }
131         }
132         let verified_device_info = match device_info {
133             Value::Map(d) => Value::Map(d),
134             other => bail!("Expected a map for verified device info, found '{:?}'", other),
135         };
136 
137         let protected_data = ProtectedData::from_cose_encrypt(
138             session,
139             encrypted_protected_data,
140             &challenge,
141             &verified_device_info,
142             &maced_keys_to_sign.tag,
143         )?;
144 
145         let verified_device_info = match verified_device_info {
146             Value::Map(m) => m,
147             _ => unreachable!("verified device info is always a map"),
148         };
149 
150         Ok(Self::V2 {
151             device_info: DeviceInfo::from_cbor_values(
152                 verified_device_info,
153                 None, // version must be determined by "version" in DeviceInfo
154                 session.options.is_factory,
155             )?,
156             challenge,
157             protected_data,
158         })
159     }
160 
v3_from_authenticated_request( session: &Session, mut csr: Vec<Value>, version: i128, ) -> Result<Self>161     fn v3_from_authenticated_request(
162         session: &Session,
163         mut csr: Vec<Value>,
164         version: i128,
165     ) -> Result<Self> {
166         if version != 1 {
167             bail!(
168                 "Invalid AuthenticatedRequest version. Only '1' is supported, found '{}",
169                 version
170             );
171         }
172 
173         // CSRs that are uploaded to the backend have an additional unverified info field tacked
174         // onto them. We just ignore that, so if it's there pop it and move on.
175         if csr.len() == 5 {
176             FieldValue::from_optional_value("UnverifiedDeviceInfo", csr.pop());
177         }
178         if csr.len() != 4 {
179             bail!("AuthenticatedRequest should have 4 elements. Found {}.", csr.len());
180         }
181 
182         let signed_data =
183             FieldValue::from_optional_value("SignedData", csr.pop()).into_cose_sign1()?;
184         let raw_dice_chain = csr.pop().ok_or(anyhow!("Missing DiceCertChain"))?;
185         let uds_certs = FieldValue::from_optional_value("UdsCerts", csr.pop()).into_map()?;
186 
187         let dice_chain = ChainForm::from_value(session, raw_dice_chain)?;
188         let uds_certs = Self::parse_and_validate_uds_certs(&dice_chain, uds_certs)?;
189 
190         let signing_key = dice_chain.leaf_public_key();
191         signing_key.verify_cose_sign1(&signed_data, &[]).context("verifying SignedData failed")?;
192 
193         let signed_data_payload = signed_data.payload.context("missing payload in SignedData")?;
194 
195         let mut signed_data_value = value_from_bytes(signed_data_payload.as_slice())
196             .context("SignedData is not valid CBOR")?;
197 
198         let signed_data_array =
199             signed_data_value.as_array_mut().context("SignedData is not a CBOR array")?;
200 
201         let csr_payload_value =
202             signed_data_array.pop().context("Missing CsrPayload in SignedData")?;
203 
204         let csr_payload = CsrPayload::from_value(&csr_payload_value, session)
205             .context("Unable to parse CsrPayload")?;
206 
207         let challenge = match signed_data_array.pop().context("missing challenge")?.into_bytes() {
208             Ok(challenge) => challenge,
209             Err(_) => bail!("Challenge is not bytes"),
210         };
211 
212         Ok(Self::V3 { dice_chain, uds_certs, challenge, csr_payload })
213     }
214 
parse_and_validate_uds_certs( dice_chain: &ChainForm, uds_certs: Vec<(Value, Value)>, ) -> Result<HashMap<String, Vec<X509>>>215     fn parse_and_validate_uds_certs(
216         dice_chain: &ChainForm,
217         uds_certs: Vec<(Value, Value)>,
218     ) -> Result<HashMap<String, Vec<X509>>> {
219         let expected_uds = match dice_chain {
220             ChainForm::Degenerate(chain) => chain.public_key(),
221             ChainForm::Proper(chain) => chain.root_public_key(),
222         }
223         .pkey();
224 
225         let mut parsed = HashMap::new();
226         for (signer, der_certs) in uds_certs {
227             let signer = FieldValue::from_value("SignerName", signer).into_string()?;
228             let x509_certs = FieldValue::from_value("UdsCertChain", der_certs)
229                 .into_array()?
230                 .into_iter()
231                 .map(|v| match FieldValue::from_value("X509Certificate", v).into_bytes() {
232                     Ok(b) => X509::from_der(&b).context("Unable to parse DER X509Certificate"),
233                     Err(e) => Err(e).context("Invalid type for X509Certificate"),
234                 })
235                 .collect::<Result<Vec<X509>>>()?;
236             Self::validate_uds_cert_path(&signer, &x509_certs)?;
237             ensure!(
238                 x509_certs.last().unwrap().public_key()?.public_eq(expected_uds),
239                 "UdsCert leaf for SignerName '{signer}' does not match the DICE chain root"
240             );
241             ensure!(
242                 parsed.insert(signer.clone(), x509_certs).is_none(),
243                 "Duplicate signer found: '{signer}'"
244             );
245         }
246         Ok(parsed)
247     }
248 
validate_uds_cert_path(signer: &String, certs: &Vec<X509>) -> Result<()>249     fn validate_uds_cert_path(signer: &String, certs: &Vec<X509>) -> Result<()> {
250         ensure!(
251             certs.len() > 1,
252             "Certificate chain for signer '{signer}' is too short: {certs:#?}"
253         );
254 
255         for cert in certs {
256             let id = cert.public_key()?.id();
257             ensure!(
258                 matches!(id, Id::RSA | Id::EC | Id::ED25519),
259                 "Certificate has an unsupported public algorithm id {id:?}"
260             );
261         }
262 
263         // OpenSSL wants us to split up root trust anchor, leaf, and intermediates
264         let mut certs_copy = certs.clone();
265         let leaf = certs_copy.pop().unwrap();
266         let mut intermediates = Stack::new()?;
267         while certs_copy.len() > 1 {
268             intermediates.push(certs_copy.pop().unwrap())?;
269         }
270         let root = certs_copy.pop().unwrap();
271 
272         let mut root_store_builder = X509StoreBuilder::new()?;
273         root_store_builder.add_cert(root)?;
274         // Setting this flag causes the signature on the root certificate to be checked.
275         // This ensures that the root certificate has not been corrupted.
276         root_store_builder.set_flags(X509VerifyFlags::CHECK_SS_SIGNATURE)?;
277 
278         let root_store = root_store_builder.build();
279 
280         let mut store = X509StoreContext::new()?;
281         let result = store.init(&root_store, &leaf, &intermediates, |context| {
282             // the with_context function must return Result<T, ErrorStack>, so we have to get
283             // tricky and return Result<Result<()>, ErrorStack> so we can bubble up custom errors.
284             match context.verify_cert() {
285                 Ok(true) => (),
286                 Ok(false) => return Ok(Err(anyhow!("Cert failed to verify: {}", context.error()))),
287                 Err(e) => return Err(e),
288             };
289 
290             if let Some(chain) = context.chain() {
291                 // OpenSSL returns the leaf at the bottom of the stack.
292                 if !chain.iter().rev().eq(certs.iter()) {
293                     let chain: Vec<_> = chain.iter().rev().map(|r| r.to_owned()).collect();
294                     return Ok(Err(anyhow!(
295                         "Verified chain doesn't match input: {chain:#?} vs {certs:#?}"
296                     )));
297                 }
298             } else {
299                 return Ok(Err(anyhow!("Cert chain is missing (impossible!)")));
300             }
301             Ok(Ok(()))
302         });
303 
304         match result {
305             Ok(e) => e,
306             Err(e) => bail!("Error verifying cert chain: {e:?}"),
307         }
308     }
309 }
310 
311 #[cfg(test)]
312 mod tests {
313     // More complete testing happens in the factorycsr module, as the test data
314     // generation spits out full JSON files, not just a CSR. Therefore, only a
315     // minimal number of smoke tests are here.
316     use super::*;
317     use crate::cbor::rkp::csr::testutil::{parse_pem_public_key_or_panic, test_device_info};
318     use crate::dice::{ChainForm, DegenerateChain, DiceMode};
319     use crate::rkp::{DeviceInfoSecurityLevel, DeviceInfoVersion};
320     use crate::session::{Options, Session};
321     use std::fs;
322 
323     #[test]
from_base64_valid_v2()324     fn from_base64_valid_v2() {
325         let input = fs::read_to_string("testdata/csr/v2_csr.base64").unwrap().trim().to_owned();
326         let csr = Csr::from_base64_cbor(&Session::default(), &input).unwrap();
327 
328         let device_info = testutil::test_device_info(DeviceInfoVersion::V2);
329         let challenge =
330             b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10".to_vec();
331         let pem = "-----BEGIN PUBLIC KEY-----\n\
332                    MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERd9pHZbUJ/b4IleUGDN8fs8+LDxE\n\
333                    vG6VX1dkw0sClFs4imbzfXGbocEq74S7TQiyZkd1LhY6HRZnTC51KoGDIA==\n\
334                    -----END PUBLIC KEY-----\n";
335         let subject_public_key = testutil::parse_pem_public_key_or_panic(pem);
336         let degenerate = ChainForm::Degenerate(
337             DegenerateChain::new("self-signed", "self-signed", subject_public_key).unwrap(),
338         );
339         let protected_data = ProtectedData::new(vec![0; 32], degenerate, None);
340         assert_eq!(csr, Csr::V2 { device_info, challenge, protected_data });
341     }
342 
343     #[test]
from_base64_valid_v3()344     fn from_base64_valid_v3() {
345         let input = fs::read_to_string("testdata/csr/v3_csr.base64").unwrap().trim().to_owned();
346         let csr = Csr::from_base64_cbor(&Session::default(), &input).unwrap();
347         if let Csr::V3 { dice_chain, uds_certs, csr_payload, .. } = csr {
348             assert_eq!(csr_payload.device_info, test_device_info(DeviceInfoVersion::V3));
349             let root_public_key = parse_pem_public_key_or_panic(
350                 "-----BEGIN PUBLIC KEY-----\n\
351                     MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh5NUV4872vKEL3XPSp8lfkV4AN3J\n\
352                 KJti1Y5kbbR9ucTpSyoOjX9UmBCM/uuPU/MGXMWrgbBf3++02ALzC+V3eQ==\n\
353                     -----END PUBLIC KEY-----\n",
354             );
355             match dice_chain {
356                 ChainForm::Proper(proper_chain) => {
357                     assert_eq!(proper_chain.root_public_key(), &root_public_key);
358                     let payloads = proper_chain.payloads();
359                     assert_eq!(payloads.len(), 1);
360                     assert_eq!(payloads[0].issuer(), "issuer");
361                     assert_eq!(payloads[0].subject(), "subject");
362                     assert_eq!(payloads[0].mode(), DiceMode::Normal);
363                     assert_eq!(payloads[0].code_hash(), &[0x55; 32]);
364                     let expected_config_hash: &[u8] =
365                         b"\xb8\x96\x54\xe2\x2c\xa4\xd2\x4a\x9c\x0e\x45\x11\xc8\xf2\x63\xf0\
366                           \x66\x0d\x2e\x20\x48\x96\x90\x14\xf4\x54\x63\xc4\xf4\x39\x30\x38";
367                     assert_eq!(payloads[0].config_hash(), Some(expected_config_hash));
368                     assert_eq!(payloads[0].authority_hash(), &[0x55; 32]);
369                     assert_eq!(payloads[0].config_desc().component_name(), Some("component_name"));
370                     assert_eq!(payloads[0].config_desc().component_version(), None);
371                     assert!(!payloads[0].config_desc().resettable());
372                     assert_eq!(payloads[0].config_desc().security_version(), None);
373                     assert_eq!(payloads[0].config_desc().extensions(), []);
374                 }
375                 ChainForm::Degenerate(d) => panic!("Parsed chain is not proper: {:?}", d),
376             }
377             assert_eq!(uds_certs.len(), 0);
378         } else {
379             panic!("Parsed CSR was not V3: {:?}", csr);
380         }
381     }
382 
383     #[test]
from_cbor_valid_v3_with_degenerate_chain() -> anyhow::Result<()>384     fn from_cbor_valid_v3_with_degenerate_chain() -> anyhow::Result<()> {
385         let cbor = fs::read("testdata/csr/v3_csr_degenerate_chain.cbor")?;
386         let session = Session { options: Options::vsr16() };
387         Csr::from_cbor(&session, cbor.as_slice())?;
388         Ok(())
389     }
390 
391     #[test]
from_cbor_valid_v3_avf_with_rkpvm_chain() -> anyhow::Result<()>392     fn from_cbor_valid_v3_avf_with_rkpvm_chain() -> anyhow::Result<()> {
393         let input = fs::read("testdata/csr/v3_csr_avf.cbor")?;
394         let mut session = Session::default();
395         session.set_allow_any_mode(true);
396         session.set_rkp_instance(RkpInstance::Avf);
397         let csr = Csr::from_cbor(&session, input.as_slice())?;
398         let Csr::V3 { dice_chain, csr_payload, .. } = csr else {
399             panic!("Parsed CSR was not V3: {:?}", csr);
400         };
401         assert_eq!(csr_payload.device_info.security_level, DeviceInfoSecurityLevel::Avf);
402         let ChainForm::Proper(proper_chain) = dice_chain else {
403             panic!("Parsed chain is not proper: {:?}", dice_chain);
404         };
405         let expected_len = 7;
406         assert_eq!(proper_chain.payloads().len(), expected_len);
407         assert!(proper_chain.payloads()[expected_len - 1].has_rkpvm_marker());
408         assert!(proper_chain.payloads()[expected_len - 2].has_rkpvm_marker());
409         Ok(())
410     }
411 
412     #[test]
from_empty_string()413     fn from_empty_string() {
414         let err = Csr::from_base64_cbor(&Session::default(), &"").unwrap_err();
415         assert!(err.to_string().contains("invalid CBOR"));
416     }
417 
418     #[test]
from_garbage()419     fn from_garbage() {
420         let err = Csr::from_base64_cbor(&Session::default(), &"cnViYmlzaAo=").unwrap_err();
421         assert!(err.to_string().contains("invalid CBOR"));
422     }
423 
424     #[test]
from_invalid_base64()425     fn from_invalid_base64() {
426         let err = Csr::from_base64_cbor(&Session::default(), &"not base64").unwrap_err();
427         assert!(err.to_string().contains("invalid base64"));
428     }
429 
430     const VALID_UDS_CHAIN: &[&str] = &[
431         "-----BEGIN CERTIFICATE-----\n\
432     MIICKjCCAbCgAwIBAgIUPFTOIhGtj7sELYJk5HicdV8r/x8wCgYIKoZIzj0EAwMw\n\
433     RzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUxGjAY\n\
434     BgNVBAMMEVRFU1QgSU5URVJNRURJQVRFMCAXDTI0MTExNDIwMTcwOFoYDzIxMjQx\n\
435     MDIxMjAxNzA4WjA/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExDzANBgNVBAoM\n\
436     Bkdvb2dsZTESMBAGA1UEAwwJVEVTVCBMRUFGMHYwEAYHKoZIzj0CAQYFK4EEACID\n\
437     YgAEry9HebgpyEnmimjtgs1KN5akdUx6cAEKVwkj0ZkYIW9V+YeRa4ap4yWvh8ZG\n\
438     U1GA0Eu26z7YQZbPuJ8LnyW0cXj3UGpXgP8EWyftWdz9EX6WpzdO7fuxtxeC/X2l\n\
439     ZuFIo2MwYTAdBgNVHQ4EFgQUWl8nH6cOAU3IrNZ2kqOzq3JUlukwHwYDVR0jBBgw\n\
440     FoAUjtSEVIqjzE6pGwlgEHPRn5o9a0YwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n\
441     Af8EBAMCB4AwCgYIKoZIzj0EAwMDaAAwZQIxAMmpFFiMRVnZHZSBCqjWQfA0lqaT\n\
442     HiusLqIEcAobDy80/mzO2yO6exNjoXkMB17COwIwD1YmiMkaqnnJkan9CNTnBXZB\n\
443     WNlU9CCE10ohcVfjssl7YVcnna70Rc1UH4DhjSj6\n\
444     -----END CERTIFICATE-----",
445         "-----BEGIN CERTIFICATE-----\n\
446     MIICLjCCAbOgAwIBAgIUKIsXCCFZRvz0BYboKmgZjyGArAEwCgYIKoZIzj0EAwMw\n\
447     PzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUxEjAQ\n\
448     BgNVBAMMCVRFU1QgUk9PVDAgFw0yNDExMTQyMDE1MzVaGA8yMTI0MTAyMTIwMTUz\n\
449     NVowRzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUx\n\
450     GjAYBgNVBAMMEVRFU1QgSU5URVJNRURJQVRFMHYwEAYHKoZIzj0CAQYFK4EEACID\n\
451     YgAEFYWPvG5PCQBBXFi/xY1F3MRqDXHkmqdTErc3wlBakVQmCjiklrEalZhMAr5Q\n\
452     0MYje5/l/ZbN+bvurD5ZsOyWRSrzTkzoUMQszB4fSoJtBp3grcEfd+/tQlC1DZO0\n\
453     wTROo2YwZDAdBgNVHQ4EFgQUjtSEVIqjzE6pGwlgEHPRn5o9a0YwHwYDVR0jBBgw\n\
454     FoAUwQ91rFNLmFq9YMlG1bqk7OvWk44wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNV\n\
455     HQ8BAf8EBAMCAgQwCgYIKoZIzj0EAwMDaQAwZgIxAMWXmsh6d8YSkP1+wR9eMDCe\n\
456     9G0EFAPOn+BiKfthnnboRUEr8BuIt3w9SkEDCdWfcAIxAMJ99xkGf3bcdykao4jh\n\
457     bgG844IvDSx11EwzQV/kcteHOut93YO0D83CgkDc2C4dNA==\n\
458     -----END CERTIFICATE-----",
459         "-----BEGIN CERTIFICATE-----\n\
460     MIICJTCCAaugAwIBAgIUUo4NdEcdRuQdrm5Trm5x+qvx2LEwCgYIKoZIzj0EAwMw\n\
461     PzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUxEjAQ\n\
462     BgNVBAMMCVRFU1QgUk9PVDAgFw0yNDExMTQyMDEzNDRaGA8yMTI0MTAyMTIwMTM0\n\
463     NFowPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUx\n\
464     EjAQBgNVBAMMCVRFU1QgUk9PVDB2MBAGByqGSM49AgEGBSuBBAAiA2IABOGIoNBS\n\
465     sVs+mTjZpqOyoWTEOIvIIhuFfi49eqleyKTnekgXyXcJfqppsbqYcgPKaTbJmhU/\n\
466     iuOjaSIUlyf5tjJ7bIOAngopcH6u+Qky/a2Q///eOIl7U9WhEMnSYwZ7rqNmMGQw\n\
467     HQYDVR0OBBYEFMEPdaxTS5havWDJRtW6pOzr1pOOMB8GA1UdIwQYMBaAFMEPdaxT\n\
468     S5havWDJRtW6pOzr1pOOMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/\n\
469     AgEBMAoGCCqGSM49BAMDA2gAMGUCMDa2TefBEmKLebf6KziawLXeQRhqb4wcMgtE\n\
470     RUZ7JOojBC6CqN7xqPMIo2Pp9Pn6iwIxANlSkus723tk6OdeG33A++HwZ9KIXzU4\n\
471     cJUsEeE4pQ5exYACy2Nd+LVtmerw8ZF6xg==\n\
472     -----END CERTIFICATE-----",
473     ];
474 
475     const INVALID_UDS_ROOT: &str = "-----BEGIN CERTIFICATE-----\n\
476     MIICJTCCAaugAwIBAgIUUo4NdEcdRuQdrm5Trm5x+qvx2LEwCgYIKoZIzj0EAwMw\n\
477     PzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUxEjAQ\n\
478     BgNVBAMMCVRFU1QgUk9PVDAgFw0yNDExMTQyMDEzNDRaGA8yMTI0MTAyMTIwMTM0\n\
479     NFowPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUx\n\
480     EjAQBgNVBAMMCVRFU1QgUk9PVDB2MBAGByqGSM49AgEGBSuBBAAiA2IABOGIoNBS\n\
481     sVs+mTjZpqOyoWTEOIvIIhuFfi49eqleyKTnekgXyXcJfqppsbqYcgPKaTbJmhU/\n\
482     iuOjaSIUlyf5tjJ7bIOAngopcH6u+Qky/a2Q///eOIl7U9WhEMnSYwZ7rqNmMGQw\n\
483     HQYDVR0OBBYEFMEPdaxTS5havWDJRtW6pOzr1pOOMB8GA1UdIwQYMBaAFMEPdaxT\n\
484     S5havWDJRtW6pOzr1pOOMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/\n\
485     AgEBMAoGCCqGSM49BAMDA2gAMGUCMDa2TefBEmKLebf6KziawLXeQRhqb4wcMgtE\n\
486     RUZ7JOojBC6CqN7xqPMIo2Pp9Pn6iwIxANlSkus723tk6OdeG33A++HwZ9KIXzU4\n\
487     cJUsEeE4pQ5exYACy2Ndthisisaproblem==\n\
488     -----END CERTIFICATE-----";
489 
490     #[test]
verify_a_valid_cert_chain()491     fn verify_a_valid_cert_chain() {
492         let leaf = X509::from_pem(VALID_UDS_CHAIN[0].as_bytes()).unwrap();
493         let intermediate = X509::from_pem(VALID_UDS_CHAIN[1].as_bytes()).unwrap();
494         let root = X509::from_pem(VALID_UDS_CHAIN[2].as_bytes()).unwrap();
495         let certs = vec![root, intermediate, leaf];
496         let signer = "Test Signer".to_string();
497         let result = Csr::validate_uds_cert_path(&signer, &certs);
498         assert!(result.is_ok());
499     }
500 
501     #[test]
make_sure_root_signature_is_checked()502     fn make_sure_root_signature_is_checked() {
503         let leaf = X509::from_pem(VALID_UDS_CHAIN[0].as_bytes()).unwrap();
504         let intermediate = X509::from_pem(VALID_UDS_CHAIN[1].as_bytes()).unwrap();
505         let valid_root = X509::from_pem(VALID_UDS_CHAIN[2].as_bytes()).unwrap();
506         let invalid_root = X509::from_pem(INVALID_UDS_ROOT.as_bytes()).unwrap();
507 
508         let valid_root_public_key = valid_root.public_key().unwrap();
509         let invalid_root_public_key = invalid_root.public_key().unwrap();
510         assert!(invalid_root_public_key.public_eq(&valid_root_public_key));
511 
512         let certs = vec![invalid_root.clone(), intermediate.clone(), leaf.clone()];
513         let signer = "Test Signer".to_string();
514         let error = Csr::validate_uds_cert_path(&signer, &certs).unwrap_err();
515         assert!(error.to_string().contains("certificate signature failure"));
516 
517         let mut intermediates = Stack::new().unwrap();
518         intermediates.push(intermediate).unwrap();
519 
520         let mut builder = X509StoreBuilder::new().unwrap();
521         builder.add_cert(invalid_root).unwrap();
522         let store = builder.build();
523 
524         let mut context = X509StoreContext::new().unwrap();
525         assert!(context.init(&store, &leaf, &intermediates, |c| c.verify_cert()).unwrap());
526     }
527 }
528 
529 #[cfg(test)]
530 pub(crate) mod testutil {
531     use crate::publickey::PublicKey;
532     use crate::rkp::{
533         DeviceInfo, DeviceInfoBootloaderState, DeviceInfoSecurityLevel, DeviceInfoVbState,
534         DeviceInfoVersion,
535     };
536     use openssl::pkey::PKey;
537 
538     // Parse the given PEM-encoded public key into a PublicKey object, panicking on failure.
parse_pem_public_key_or_panic(pem: &str) -> PublicKey539     pub fn parse_pem_public_key_or_panic(pem: &str) -> PublicKey {
540         PKey::public_key_from_pem(pem.as_bytes()).unwrap().try_into().unwrap()
541     }
542 
543     // The test data uses mostly common DeviceInfo fields
test_device_info(version: DeviceInfoVersion) -> DeviceInfo544     pub fn test_device_info(version: DeviceInfoVersion) -> DeviceInfo {
545         DeviceInfo {
546             version,
547             brand: "Google".to_string(),
548             manufacturer: "Google".to_string(),
549             product: "pixel".to_string(),
550             model: "model".to_string(),
551             device: "device".to_string(),
552             vb_state: DeviceInfoVbState::Green,
553             bootloader_state: DeviceInfoBootloaderState::Locked,
554             vbmeta_digest: b"\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff".to_vec(),
555             os_version: Some("12".to_string()),
556             system_patch_level: 20221025,
557             boot_patch_level: 20221026,
558             vendor_patch_level: 20221027,
559             security_level: DeviceInfoSecurityLevel::Tee,
560             fused: 1,
561         }
562     }
563 }
564