1 use crate::rkp::Csr;
2 use crate::session::Session;
3 use anyhow::{bail, Result};
4 use serde_json::{Map, Value};
5 
6 /// Represents a "Factory CSR", which is a JSON value captured for each device on the factory
7 /// line. This JSON is uploaded to the RKP backend to register the device. We reuse the CSR
8 /// (Certificate Signing Request) format for this as an implementation convenience. The CSR
9 /// actually contains an empty set of keys for which certificates are needed.
10 #[non_exhaustive]
11 #[derive(Debug, Eq, PartialEq)]
12 pub struct FactoryCsr {
13     /// The CSR, as created by an IRemotelyProvisionedComponent HAL.
14     pub csr: Csr,
15     /// The name of the HAL that generated the CSR.
16     pub name: String,
17 }
18 
get_string_from_map(fields: &Map<String, Value>, key: &str) -> Result<String>19 fn get_string_from_map(fields: &Map<String, Value>, key: &str) -> Result<String> {
20     match fields.get(key) {
21         Some(Value::String(s)) => Ok(s.to_string()),
22         Some(v) => bail!("Unexpected type for '{key}'. Expected String, found '{v:?}'"),
23         None => bail!("Unable to locate '{key}' in input"),
24     }
25 }
26 
27 impl FactoryCsr {
28     /// Parse the input JSON string into a CSR that was captured on the factory line. The
29     /// format of the JSON data is defined by rkp_factory_extraction_tool.
from_json(session: &Session, json: &str) -> Result<Self>30     pub fn from_json(session: &Session, json: &str) -> Result<Self> {
31         match serde_json::from_str(json) {
32             Ok(Value::Object(map)) => Self::from_map(session, map),
33             Ok(unexpected) => bail!("Expected a map, got some other type: {unexpected}"),
34             Err(e) => bail!("Error parsing input json: {e}"),
35         }
36     }
37 
from_map(session: &Session, fields: Map<String, Value>) -> Result<Self>38     fn from_map(session: &Session, fields: Map<String, Value>) -> Result<Self> {
39         let base64 = get_string_from_map(&fields, "csr")?;
40         let name = get_string_from_map(&fields, "name")?;
41         let csr = Csr::from_base64_cbor(session, &base64)?;
42         Ok(Self { csr, name })
43     }
44 }
45 
46 #[cfg(test)]
47 mod tests {
48     use super::*;
49     use crate::cbor::rkp::csr::testutil::{parse_pem_public_key_or_panic, test_device_info};
50     use crate::dice::{ChainForm, DegenerateChain};
51     use crate::rkp::device_info::DeviceInfoVersion;
52     use crate::rkp::factory_csr::FactoryCsr;
53     use crate::rkp::{ProtectedData, UdsCerts, UdsCertsEntry};
54     use anyhow::anyhow;
55     use itertools::Itertools;
56     use openssl::{pkey::PKey, x509::X509};
57     use std::fs;
58     use std::fs::File;
59 
json_map_from_file(path: &str) -> Result<Map<String, Value>>60     fn json_map_from_file(path: &str) -> Result<Map<String, Value>> {
61         let input = File::open(path)?;
62         match serde_json::from_reader(input)? {
63             Value::Object(map) => Ok(map),
64             other => Err(anyhow!("Unexpected JSON. Wanted a map, found {other:?}")),
65         }
66     }
67 
68     #[test]
from_json_valid_v2_ed25519()69     fn from_json_valid_v2_ed25519() {
70         let json = fs::read_to_string("testdata/factory_csr/v2_ed25519_valid.json").unwrap();
71         let csr = FactoryCsr::from_json(&Session::default(), &json).unwrap();
72         let subject_public_key = parse_pem_public_key_or_panic(
73             "-----BEGIN PUBLIC KEY-----\n\
74             MCowBQYDK2VwAyEAOhWsfxcBLgUfLLdqpb8cLUWutkkPtfIqDRfJC3LUihI=\n\
75             -----END PUBLIC KEY-----\n",
76         );
77         let degenerate = ChainForm::Degenerate(
78             DegenerateChain::new("self-signed", "self-signed", subject_public_key).unwrap(),
79         );
80         let chain = [
81             "-----BEGIN CERTIFICATE-----\n\
82              MIIBaDCCARqgAwIBAgIBezAFBgMrZXAwKzEVMBMGA1UEChMMRmFrZSBDb21wYW55\n\
83              MRIwEAYDVQQDEwlGYWtlIFJvb3QwHhcNMjMwODAxMjI1MTM0WhcNMjMwODMxMjI1\n\
84              MTM0WjArMRUwEwYDVQQKEwxGYWtlIENvbXBhbnkxEjAQBgNVBAMTCUZha2UgUm9v\n\
85              dDAqMAUGAytlcAMhAJYhPNIeQXe6+GPFiQAg4WtK+D8HuWaF6Es4X3HgDzq7o2Mw\n\
86              YTAdBgNVHQ4EFgQUDR0DF3abDeR3WXSlIhpN07R049owHwYDVR0jBBgwFoAUDR0D\n\
87              F3abDeR3WXSlIhpN07R049owDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n\
88              AgQwBQYDK2VwA0EAWNEhXrATWj4MXT/n38OwbngUm/n/5+vGFqV0aXuJPX/8d6Yx\n\
89              BbX/LFv6m8/VuPuItSqK4AudgwZJoupR/lknDg==\n\
90              -----END CERTIFICATE-----\n",
91             "-----BEGIN CERTIFICATE-----\n\
92              MIIBbDCCAR6gAwIBAgICAcgwBQYDK2VwMCsxFTATBgNVBAoTDEZha2UgQ29tcGFu\n\
93              eTESMBAGA1UEAxMJRmFrZSBSb290MB4XDTIzMDgwMTIyNTEzNFoXDTIzMDgzMTIy\n\
94              NTEzNFowLjEVMBMGA1UEChMMRmFrZSBDb21wYW55MRUwEwYDVQQDEwxGYWtlIENo\n\
95              aXBzZXQwKjAFBgMrZXADIQA6Fax/FwEuBR8st2qlvxwtRa62SQ+18ioNF8kLctSK\n\
96              EqNjMGEwHQYDVR0OBBYEFEbOrkgBL2SUCLJayyvpc+oR7m6/MB8GA1UdIwQYMBaA\n\
97              FA0dAxd2mw3kd1l0pSIaTdO0dOPaMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n\
98              BAQDAgIEMAUGAytlcANBANJmKGgiZP3tqtjnHpbmR3ypkXtvuqmo6KTBIHKsGKAO\n\
99              mH8qlMQPLmNSdxTs3OnVrlxQwZqXxHjVHS/6OpkkFgo=\n\
100              -----END CERTIFICATE-----\n",
101         ];
102         let chain = chain
103             .iter()
104             .map(|pem| X509::from_pem(pem.as_bytes()).unwrap().to_der().unwrap())
105             .collect_vec();
106 
107         let mut uds_certs = UdsCerts::new();
108         uds_certs
109             .0
110             .insert("google-test".to_string(), UdsCertsEntry::new_x509_chain(chain).unwrap());
111         assert_eq!(
112             csr,
113             FactoryCsr {
114                 csr: Csr::V2 {
115                     device_info: test_device_info(DeviceInfoVersion::V2),
116                     challenge: b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
117                         .to_vec(),
118                     protected_data: ProtectedData::new(vec![0; 32], degenerate, Some(uds_certs)),
119                 },
120                 name: "default".to_string(),
121             }
122         );
123     }
124 
125     #[test]
from_json_valid_v3_ed25519()126     fn from_json_valid_v3_ed25519() {
127         let json = fs::read_to_string("testdata/factory_csr/v3_ed25519_valid.json").unwrap();
128         let csr = FactoryCsr::from_json(&Session::default(), &json).unwrap();
129         if let Csr::V3 { device_info, dice_chain } = csr.csr {
130             assert_eq!(device_info, test_device_info(DeviceInfoVersion::V3));
131             let root_public_key = parse_pem_public_key_or_panic(
132                 "-----BEGIN PUBLIC KEY-----\n\
133                 MCowBQYDK2VwAyEA3FEn/nhqoGOKNok1AJaLfTKI+aFXHf4TfC42vUyPU6s=\n\
134                 -----END PUBLIC KEY-----\n",
135             );
136             assert_eq!(dice_chain.root_public_key(), &root_public_key);
137             assert_eq!(dice_chain.payloads().len(), 1);
138         } else {
139             panic!("Parsed CSR was not V3: {:?}", csr);
140         }
141     }
142 
143     #[test]
from_json_valid_v2_p256()144     fn from_json_valid_v2_p256() {
145         let json = fs::read_to_string("testdata/factory_csr/v2_p256_valid.json").unwrap();
146         let csr = FactoryCsr::from_json(&Session::default(), &json).unwrap();
147         let pem = "-----BEGIN PUBLIC KEY-----\n\
148                    MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERd9pHZbUJ/b4IleUGDN8fs8+LDxE\n\
149                    vG6VX1dkw0sClFs4imbzfXGbocEq74S7TQiyZkd1LhY6HRZnTC51KoGDIA==\n\
150                    -----END PUBLIC KEY-----\n";
151         let subject_public_key =
152             PKey::public_key_from_pem(pem.as_bytes()).unwrap().try_into().unwrap();
153         let degenerate = ChainForm::Degenerate(
154             DegenerateChain::new("self-signed", "self-signed", subject_public_key).unwrap(),
155         );
156         assert_eq!(
157             csr,
158             FactoryCsr {
159                 csr: Csr::V2 {
160                     device_info: test_device_info(DeviceInfoVersion::V2),
161                     challenge: b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
162                         .to_vec(),
163                     protected_data: ProtectedData::new(vec![0; 32], degenerate, None),
164                 },
165                 name: "default".to_string(),
166             }
167         );
168     }
169 
170     #[test]
from_json_valid_v3_p256()171     fn from_json_valid_v3_p256() {
172         let json = fs::read_to_string("testdata/factory_csr/v3_p256_valid.json").unwrap();
173         let csr = FactoryCsr::from_json(&Session::default(), &json).unwrap();
174         if let Csr::V3 { device_info, dice_chain } = csr.csr {
175             assert_eq!(device_info, test_device_info(DeviceInfoVersion::V3));
176             let root_public_key = parse_pem_public_key_or_panic(
177                 "-----BEGIN PUBLIC KEY-----\n\
178                 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqT6ujVegwBbVWtsZeZmvN4WO3THx\n\
179                 zpPPnt2rAOdqL9DSDZcIBbLas5xh9psaEaD0o/0KxlUVZplO/BPmRf3Ycg==\n\
180                 -----END PUBLIC KEY-----\n",
181             );
182             assert_eq!(dice_chain.root_public_key(), &root_public_key);
183             assert_eq!(dice_chain.payloads().len(), 1);
184         } else {
185             panic!("Parsed CSR was not V3: {:?}", csr);
186         }
187     }
188 
189     #[test]
from_json_name_is_missing()190     fn from_json_name_is_missing() {
191         let mut value = json_map_from_file("testdata/factory_csr/v2_ed25519_valid.json").unwrap();
192         value.remove_entry("name");
193         let json = serde_json::to_string(&value).unwrap();
194         let err = FactoryCsr::from_json(&Session::default(), &json).unwrap_err();
195         assert!(err.to_string().contains("Unable to locate 'name'"));
196     }
197 
198     #[test]
from_json_name_is_wrong_type()199     fn from_json_name_is_wrong_type() {
200         let mut value = json_map_from_file("testdata/factory_csr/v2_ed25519_valid.json").unwrap();
201         value.insert("name".to_string(), Value::Object(Map::default()));
202         let json = serde_json::to_string(&value).unwrap();
203         let err = FactoryCsr::from_json(&Session::default(), &json).unwrap_err();
204         assert!(err.to_string().contains("Unexpected type for 'name'"));
205     }
206 
207     #[test]
from_json_csr_is_missing()208     fn from_json_csr_is_missing() {
209         let json = r#"{ "name": "default" }"#;
210         let err = FactoryCsr::from_json(&Session::default(), json).unwrap_err();
211         assert!(err.to_string().contains("Unable to locate 'csr'"));
212     }
213 
214     #[test]
from_json_csr_is_wrong_type()215     fn from_json_csr_is_wrong_type() {
216         let json = r#"{ "csr": 3.1415, "name": "default" }"#;
217         let err = FactoryCsr::from_json(&Session::default(), json).unwrap_err();
218         assert!(err.to_string().contains("Unexpected type for 'csr'"));
219     }
220 
221     #[test]
from_json_extra_tag_is_ignored()222     fn from_json_extra_tag_is_ignored() {
223         let mut value = json_map_from_file("testdata/factory_csr/v2_ed25519_valid.json").unwrap();
224         value.insert("extra".to_string(), Value::Bool(true));
225         let json = serde_json::to_string(&value).unwrap();
226         let csr = FactoryCsr::from_json(&Session::default(), &json).unwrap();
227         assert_eq!(csr.name, "default");
228     }
229 }
230