• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use coset::CoseKey;
2 use openssl::x509::X509;
3 use std::{collections::HashMap, fmt};
4 use thiserror::Error;
5 
6 use crate::dice::ChainForm;
7 
8 use super::{DeviceInfo, ProtectedData, UdsCerts};
9 
10 /// Represents the keys to sign that are to be signed
11 #[derive(Clone, Debug, PartialEq)]
12 pub struct KeysToSign(pub Vec<CoseKey>);
13 
14 /// Represents the payload of a Certificate Signing Request
15 #[derive(Clone, PartialEq)]
16 pub struct CsrPayload {
17     /// The original serialized CSR payload
18     pub serialized: Vec<u8>,
19     /// RKP VM or other?
20     pub certificate_type: String,
21     /// Describes the device that is requesting certificates.
22     pub device_info: DeviceInfo,
23     /// The keys to attest to when doing key attestation in one buffer
24     pub keys_to_sign: KeysToSign,
25 }
26 
27 /// Represents a Certificate Signing Request that is sent to an RKP backend to request
28 /// certificates to be signed for a set of public keys. The CSR is partially generated by an
29 /// IRemotelyProvisionedComponent HAL. The set of public keys to be signed is authenticated
30 /// (signed) with a device-unique key.
31 #[derive(Clone, PartialEq)]
32 #[allow(clippy::large_enum_variant)]
33 pub enum Csr {
34     /// CSR V2 was introduced in Android T. In this version, the payload is encrypted using
35     /// an Endpoint Encryption Key (EEK).
36     V2 {
37         /// Describes the device that is requesting certificates.
38         device_info: DeviceInfo,
39         /// This is the challenge that is authenticated inside the protected data.
40         challenge: Vec<u8>,
41         /// Contains the plaintext of the payload that was encrypted to an EEK.
42         protected_data: ProtectedData,
43     },
44     /// CSR V3 was introduced in Android U. This version drops encryption of the payload.
45     V3 {
46         /// The DICE chain for the device
47         dice_chain: ChainForm,
48         /// X.509 certificate chain that certifies the dice_chain root key (UDS_pub)
49         uds_certs: HashMap<String, Vec<X509>>,
50         /// The challenge that is authenticated inside the signed data.
51         challenge: Vec<u8>,
52         /// The payload of the signed data.
53         csr_payload: CsrPayload,
54     },
55 }
56 
57 impl Csr {
58     /// copy the DICE chain and return it
dice_chain(&self) -> ChainForm59     pub fn dice_chain(&self) -> ChainForm {
60         match self {
61             Csr::V2 { protected_data, .. } => protected_data.dice_chain(),
62             Csr::V3 { dice_chain, .. } => dice_chain.clone(),
63         }
64     }
65 
66     /// copy the UDS certs map and return it
has_uds_certs(&self) -> bool67     pub fn has_uds_certs(&self) -> bool {
68         match self {
69             Csr::V2 { protected_data, .. } => match protected_data.uds_certs() {
70                 Some(uds_certs) => match uds_certs {
71                     UdsCerts(map) => !map.is_empty(),
72                 },
73                 None => false,
74             },
75             Csr::V3 { uds_certs, .. } => !uds_certs.is_empty(),
76         }
77     }
78 
79     /// copy the challenge and return it
challenge(&self) -> Vec<u8>80     pub fn challenge(&self) -> Vec<u8> {
81         match self {
82             Csr::V2 { challenge, .. } => challenge.clone(),
83             Csr::V3 { challenge, .. } => challenge.clone(),
84         }
85     }
86 
87     /// copy the serialized CSR payload and return it
csr_payload(&self) -> Vec<u8>88     pub fn csr_payload(&self) -> Vec<u8> {
89         match self {
90             Csr::V2 { .. } => Vec::new(),
91             Csr::V3 { csr_payload, .. } => csr_payload.serialized.clone(),
92         }
93     }
94 
95     /// copy the device info and return it
compare_keys_to_sign(&self, keys_to_sign: &[u8]) -> bool96     pub fn compare_keys_to_sign(&self, keys_to_sign: &[u8]) -> bool {
97         let keys_to_sign = match KeysToSign::from_bytes(keys_to_sign) {
98             Ok(keys_to_sign) => keys_to_sign,
99             Err(_) => return false,
100         };
101 
102         match self {
103             Csr::V2 { .. } => false,
104             Csr::V3 { csr_payload, .. } => csr_payload.keys_to_sign == keys_to_sign,
105         }
106     }
107 }
108 
109 impl fmt::Debug for Csr {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result110     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
111         match self {
112             Csr::V2 { device_info, challenge, protected_data } => fmt
113                 .debug_struct("CSR V2")
114                 .field("DeviceInfo", &device_info)
115                 .field("Challenge", &hex::encode(challenge))
116                 .field("ProtectedData", &protected_data)
117                 .finish(),
118             Csr::V3 { dice_chain, uds_certs, csr_payload, .. } => fmt
119                 .debug_struct("CSR V3")
120                 .field("DeviceInfo", &csr_payload.device_info)
121                 .field("DiceChain", &dice_chain)
122                 .field("UdsCerts", &uds_certs)
123                 .finish(),
124         }
125     }
126 }
127 
128 /// Builder errors for Csr V2 and V3.
129 #[derive(Debug, PartialEq, Error)]
130 pub enum CsrBuilderError {
131     /// Device info is missing.
132     #[error("Missing device info")]
133     MissingDeviceInfo,
134     /// Challenge is missing.
135     #[error("Missing challenge")]
136     MissingChallenge,
137     /// Protected data is missing.
138     #[error("Missing protected data")]
139     MissingProtectedData,
140     /// DICE chain is missing.
141     #[error("Missing DICE chain")]
142     MissingDiceChain,
143     /// CSR payload is missing.
144     #[error("Missing CSR payload")]
145     MissingCsrPayload,
146     /// UDS certificates are missing.
147     #[error("Missing UDS certificates")]
148     MissingUdsCerts,
149 }
150 
151 /// Builder for Csr::V2.
152 #[derive(Default)]
153 pub struct CsrV2Builder {
154     device_info: Option<DeviceInfo>,
155     challenge: Option<Vec<u8>>,
156     protected_data: Option<ProtectedData>,
157 }
158 
159 impl CsrV2Builder {
160     /// Builds the CSR V2.
build(self) -> Result<Csr, CsrBuilderError>161     pub fn build(self) -> Result<Csr, CsrBuilderError> {
162         let device_info = self.device_info.ok_or(CsrBuilderError::MissingDeviceInfo)?;
163         let challenge = self.challenge.ok_or(CsrBuilderError::MissingChallenge)?;
164         let protected_data = self.protected_data.ok_or(CsrBuilderError::MissingProtectedData)?;
165 
166         Ok(Csr::V2 { device_info, challenge, protected_data })
167     }
168 
169     /// Sets the device info.
170     #[must_use]
device_info(mut self, device_info: DeviceInfo) -> Self171     pub fn device_info(mut self, device_info: DeviceInfo) -> Self {
172         self.device_info = Some(device_info);
173         self
174     }
175 
176     /// Sets the challenge.
177     #[must_use]
challenge(mut self, challenge: Vec<u8>) -> Self178     pub fn challenge(mut self, challenge: Vec<u8>) -> Self {
179         self.challenge = Some(challenge);
180         self
181     }
182 
183     /// Sets the protected data.
184     #[must_use]
protected_data(mut self, protected_data: ProtectedData) -> Self185     pub fn protected_data(mut self, protected_data: ProtectedData) -> Self {
186         self.protected_data = Some(protected_data);
187         self
188     }
189 }
190 
191 /// Builder for Csr::V3.
192 #[derive(Default)]
193 pub struct CsrV3Builder {
194     challenge: Option<Vec<u8>>,
195     dice_chain: Option<ChainForm>,
196     uds_certs: Option<HashMap<String, Vec<X509>>>,
197     csr_payload: Option<CsrPayload>,
198 }
199 
200 impl CsrV3Builder {
201     /// Builds Csr::V3.
build(self) -> Result<Csr, CsrBuilderError>202     pub fn build(self) -> Result<Csr, CsrBuilderError> {
203         let challenge = self.challenge.ok_or(CsrBuilderError::MissingChallenge)?;
204         let dice_chain = self.dice_chain.ok_or(CsrBuilderError::MissingDiceChain)?;
205         let uds_certs = self.uds_certs.ok_or(CsrBuilderError::MissingUdsCerts)?;
206         let csr_payload = self.csr_payload.ok_or(CsrBuilderError::MissingCsrPayload)?;
207 
208         Ok(Csr::V3 { dice_chain, uds_certs, challenge, csr_payload })
209     }
210 
211     /// Sets the challenge.
212     #[must_use]
challenge(mut self, challenge: Vec<u8>) -> Self213     pub fn challenge(mut self, challenge: Vec<u8>) -> Self {
214         self.challenge = Some(challenge);
215         self
216     }
217 
218     /// Sets the DICE chain.
219     #[must_use]
dice_chain(mut self, dice_chain: ChainForm) -> Self220     pub fn dice_chain(mut self, dice_chain: ChainForm) -> Self {
221         self.dice_chain = Some(dice_chain);
222         self
223     }
224 
225     /// Sets the UDS certificates.
226     #[must_use]
uds_certs(mut self, uds_certs: HashMap<String, Vec<X509>>) -> Self227     pub fn uds_certs(mut self, uds_certs: HashMap<String, Vec<X509>>) -> Self {
228         self.uds_certs = Some(uds_certs);
229         self
230     }
231 
232     /// Sets the CSR payload.
233     #[must_use]
csr_payload(mut self, csr_payload: CsrPayload) -> Self234     pub fn csr_payload(mut self, csr_payload: CsrPayload) -> Self {
235         self.csr_payload = Some(csr_payload);
236         self
237     }
238 }
239 
240 #[cfg(test)]
241 mod tests {
242     use super::*;
243     use crate::cbor::rkp::csr::testutil::{parse_pem_public_key_or_panic, test_device_info};
244     use crate::dice::{ChainForm, DegenerateChain};
245     use crate::rkp::device_info::DeviceInfoVersion;
246     use crate::rkp::protected_data::ProtectedData;
247     use anyhow::{Context, Result};
248     use coset::{iana, CoseKey, CoseKeyBuilder};
249     use openssl::bn::BigNum;
250 
create_test_key() -> Result<CoseKey>251     fn create_test_key() -> Result<CoseKey> {
252         let x = BigNum::from_u32(1234).context("Failed to create x coord")?;
253         let y = BigNum::from_u32(4321).context("Failed to create y coord")?;
254         Ok(CoseKeyBuilder::new_ec2_pub_key(iana::EllipticCurve::P_256, x.to_vec(), y.to_vec())
255             .build())
256     }
257 
258     #[test]
build_and_debug_csr_v2()259     fn build_and_debug_csr_v2() {
260         let device_info = test_device_info(DeviceInfoVersion::V2);
261         let challenge = b"challenge".to_vec();
262         let root_public_key = parse_pem_public_key_or_panic(
263             "-----BEGIN PUBLIC KEY-----\n\
264                 MCowBQYDK2VwAyEArqr7jIIQ8TB1+l/Sh69eiSJL6t6txO1oLhpkdVSUuBk=\n\
265                 -----END PUBLIC KEY-----\n",
266         );
267 
268         let degenerate_chain = DegenerateChain::new("test_issuer", "test_subject", root_public_key)
269             .expect("Failed to create certificate chain");
270         let protected_data =
271             ProtectedData::new(vec![0; 32], ChainForm::Degenerate(degenerate_chain), None);
272 
273         let csr = CsrV2Builder::default()
274             .device_info(device_info.clone())
275             .challenge(challenge.clone())
276             .protected_data(protected_data.clone())
277             .build()
278             .expect("Failed to build CSR V2");
279 
280         let expected = format!(
281             "CSR V2 {{ DeviceInfo: {device_info:?}, Challenge: {:?}, ProtectedData: {protected_data:?} }}",
282             hex::encode(&challenge)
283         );
284 
285         assert_eq!(format!("{csr:?}"), expected);
286     }
287 
288     #[test]
build_and_debug_csr_v3()289     fn build_and_debug_csr_v3() {
290         let device_info = test_device_info(DeviceInfoVersion::V3);
291 
292         let challenge = b"challenge".to_vec();
293 
294         let serialized_payload = b"serialized_payload".to_vec();
295         let certificate_type = "test_certificate_type".to_string();
296         let mut keys_to_sign_vec = Vec::new();
297         let key = create_test_key().expect("Failed to create test key");
298         keys_to_sign_vec.push(key);
299 
300         let keys_to_sign = KeysToSign(keys_to_sign_vec);
301 
302         let csr_payload = CsrPayload {
303             serialized: serialized_payload,
304             certificate_type,
305             device_info: device_info.clone(),
306             keys_to_sign,
307         };
308         let root_public_key = parse_pem_public_key_or_panic(
309             "-----BEGIN PUBLIC KEY-----\n\
310                 MCowBQYDK2VwAyEArqr7jIIQ8TB1+l/Sh69eiSJL6t6txO1oLhpkdVSUuBk=\n\
311                 -----END PUBLIC KEY-----\n",
312         );
313         let degenerate_chain = DegenerateChain::new("test_issuer", "test_subject", root_public_key)
314             .expect("Failed to create certificate chain");
315         let dice_chain = ChainForm::Degenerate(degenerate_chain);
316         let uds_certs = HashMap::new();
317 
318         let csr = CsrV3Builder::default()
319             .challenge(challenge.clone())
320             .dice_chain(dice_chain.clone())
321             .uds_certs(uds_certs.clone())
322             .csr_payload(csr_payload.clone())
323             .build()
324             .expect("Failed to build CSR V3");
325 
326         let expected = format!(
327             "CSR V3 {{ DeviceInfo: {device_info:?}, DiceChain: {dice_chain:?}, UdsCerts: {uds_certs:?} }}",
328         );
329 
330         assert_eq!(format!("{csr:?}"), expected);
331     }
332 }
333