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