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