1 use std::{fmt, str::FromStr}; 2 3 use rpki::{ 4 repository::{ 5 cert::Cert, 6 crypto::{KeyIdentifier, PublicKey}, 7 csr::Csr, 8 resources::{AsBlocks, IpBlocks, IpBlocksForFamily}, 9 x509::Time, 10 }, 11 uri, 12 }; 13 14 use crate::commons::{ 15 api::ca::{IssuedCert, RcvdCert, ResourceClassName, ResourceSet}, 16 util::ext_serde, 17 }; 18 19 //------------ ProvisioningRequest ------------------------------------------- 20 21 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 22 #[allow(clippy::large_enum_variant)] 23 pub enum ProvisioningRequest { 24 List, 25 Request(IssuanceRequest), 26 } 27 28 impl ProvisioningRequest { list() -> Self29 pub fn list() -> Self { 30 ProvisioningRequest::List 31 } request(r: IssuanceRequest) -> Self32 pub fn request(r: IssuanceRequest) -> Self { 33 ProvisioningRequest::Request(r) 34 } 35 } 36 37 //------------ ProvisioningResponse ----------------------------------------- 38 39 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 40 pub enum ProvisioningResponse { 41 List(Entitlements), 42 } 43 44 //------------ Entitlements ------------------------------------------------- 45 46 /// This structure is what is called the "Resource Class List Response" 47 /// in section 3.3.2 of RFC6492. 48 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 49 pub struct Entitlements { 50 classes: Vec<EntitlementClass>, 51 } 52 53 impl Entitlements { with_default_class( issuer: SigningCert, resource_set: ResourceSet, not_after: Time, issued: Vec<IssuedCert>, ) -> Self54 pub fn with_default_class( 55 issuer: SigningCert, 56 resource_set: ResourceSet, 57 not_after: Time, 58 issued: Vec<IssuedCert>, 59 ) -> Self { 60 Entitlements { 61 classes: vec![EntitlementClass { 62 class_name: ResourceClassName::default(), 63 issuer, 64 resource_set, 65 not_after, 66 issued, 67 }], 68 } 69 } new(classes: Vec<EntitlementClass>) -> Self70 pub fn new(classes: Vec<EntitlementClass>) -> Self { 71 Entitlements { classes } 72 } 73 classes(&self) -> &Vec<EntitlementClass>74 pub fn classes(&self) -> &Vec<EntitlementClass> { 75 &self.classes 76 } 77 } 78 79 impl fmt::Display for Entitlements { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 81 let classes: Vec<String> = self.classes.iter().map(EntitlementClass::to_string).collect(); 82 83 let classes = classes.join(", "); 84 85 write!(f, "{}", classes) 86 } 87 } 88 89 //------------ EntitlementClass ---------------------------------------------- 90 91 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 92 pub struct EntitlementClass { 93 class_name: ResourceClassName, 94 issuer: SigningCert, 95 resource_set: ResourceSet, 96 not_after: Time, 97 issued: Vec<IssuedCert>, 98 } 99 100 impl EntitlementClass { new( class_name: ResourceClassName, issuer: SigningCert, resource_set: ResourceSet, not_after: Time, issued: Vec<IssuedCert>, ) -> Self101 pub fn new( 102 class_name: ResourceClassName, 103 issuer: SigningCert, 104 resource_set: ResourceSet, 105 not_after: Time, 106 issued: Vec<IssuedCert>, 107 ) -> Self { 108 EntitlementClass { 109 class_name, 110 issuer, 111 resource_set, 112 not_after, 113 issued, 114 } 115 } 116 unwrap(self) -> (ResourceClassName, SigningCert, ResourceSet, Time, Vec<IssuedCert>)117 fn unwrap(self) -> (ResourceClassName, SigningCert, ResourceSet, Time, Vec<IssuedCert>) { 118 ( 119 self.class_name, 120 self.issuer, 121 self.resource_set, 122 self.not_after, 123 self.issued, 124 ) 125 } 126 class_name(&self) -> &ResourceClassName127 pub fn class_name(&self) -> &ResourceClassName { 128 &self.class_name 129 } 130 issuer(&self) -> &SigningCert131 pub fn issuer(&self) -> &SigningCert { 132 &self.issuer 133 } 134 resource_set(&self) -> &ResourceSet135 pub fn resource_set(&self) -> &ResourceSet { 136 &self.resource_set 137 } 138 not_after(&self) -> Time139 pub fn not_after(&self) -> Time { 140 self.not_after 141 } 142 issued(&self) -> &Vec<IssuedCert>143 pub fn issued(&self) -> &Vec<IssuedCert> { 144 &self.issued 145 } 146 147 /// Converts this into an IssuanceResponse for the given key. I.e. includes 148 /// the issued certificate matching the given public key only. Returns a 149 /// None if no match is found. into_issuance_response(self, key: &PublicKey) -> Option<IssuanceResponse>150 pub fn into_issuance_response(self, key: &PublicKey) -> Option<IssuanceResponse> { 151 let (class_name, issuer, resource_set, not_after, issued) = self.unwrap(); 152 153 issued 154 .into_iter() 155 .find(|issued| issued.cert().subject_public_key_info() == key) 156 .map(|issued| IssuanceResponse::new(class_name, issuer, resource_set, not_after, issued)) 157 } 158 } 159 160 impl fmt::Display for EntitlementClass { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 162 let issued: Vec<String> = self 163 .issued 164 .iter() 165 .map(|c| c.cert().subject_key_identifier().to_string()) 166 .collect(); 167 168 let issued = issued.join(","); 169 170 write!( 171 f, 172 "class name '{}' issuing key '{}' resources '{}' issued '{}'", 173 self.class_name, 174 self.issuer.cert.subject_key_identifier(), 175 self.resource_set, 176 issued 177 ) 178 } 179 } 180 181 //------------ SigningCert --------------------------------------------------- 182 183 #[derive(Clone, Debug, Deserialize, Serialize)] 184 pub struct SigningCert { 185 uri: uri::Rsync, 186 cert: Cert, 187 } 188 189 impl SigningCert { new(uri: uri::Rsync, cert: Cert) -> Self190 pub fn new(uri: uri::Rsync, cert: Cert) -> Self { 191 SigningCert { uri, cert } 192 } 193 uri(&self) -> &uri::Rsync194 pub fn uri(&self) -> &uri::Rsync { 195 &self.uri 196 } cert(&self) -> &Cert197 pub fn cert(&self) -> &Cert { 198 &self.cert 199 } 200 } 201 202 impl PartialEq for SigningCert { eq(&self, other: &SigningCert) -> bool203 fn eq(&self, other: &SigningCert) -> bool { 204 self.uri == other.uri && self.cert.to_captured().as_slice() == other.cert.to_captured().as_slice() 205 } 206 } 207 208 impl Eq for SigningCert {} 209 210 impl From<&RcvdCert> for SigningCert { from(c: &RcvdCert) -> Self211 fn from(c: &RcvdCert) -> Self { 212 SigningCert { 213 uri: c.uri().clone(), 214 cert: c.cert().clone(), 215 } 216 } 217 } 218 219 //------------ IssuanceRequest ----------------------------------------------- 220 221 /// This type reflects the content of a Certificate Issuance Request 222 /// defined in section 3.4.1 of RFC6492. 223 #[derive(Clone, Debug, Deserialize, Serialize)] 224 pub struct IssuanceRequest { 225 class_name: ResourceClassName, 226 limit: RequestResourceLimit, 227 csr: Csr, 228 } 229 230 impl IssuanceRequest { new(class_name: ResourceClassName, limit: RequestResourceLimit, csr: Csr) -> Self231 pub fn new(class_name: ResourceClassName, limit: RequestResourceLimit, csr: Csr) -> Self { 232 IssuanceRequest { class_name, limit, csr } 233 } 234 unpack(self) -> (ResourceClassName, RequestResourceLimit, Csr)235 pub fn unpack(self) -> (ResourceClassName, RequestResourceLimit, Csr) { 236 (self.class_name, self.limit, self.csr) 237 } 238 class_name(&self) -> &ResourceClassName239 pub fn class_name(&self) -> &ResourceClassName { 240 &self.class_name 241 } limit(&self) -> &RequestResourceLimit242 pub fn limit(&self) -> &RequestResourceLimit { 243 &self.limit 244 } csr(&self) -> &Csr245 pub fn csr(&self) -> &Csr { 246 &self.csr 247 } 248 } 249 250 impl PartialEq for IssuanceRequest { eq(&self, other: &IssuanceRequest) -> bool251 fn eq(&self, other: &IssuanceRequest) -> bool { 252 self.class_name == other.class_name 253 && self.limit == other.limit 254 && self.csr.to_captured().as_slice() == other.csr.to_captured().as_slice() 255 } 256 } 257 258 impl Eq for IssuanceRequest {} 259 260 impl fmt::Display for IssuanceRequest { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result261 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 262 let ki = self.csr.public_key().key_identifier(); 263 let none = "<none>".to_string(); 264 let rpki_notify = self 265 .csr 266 .rpki_notify() 267 .map(uri::Https::to_string) 268 .unwrap_or_else(|| none.clone()); 269 let ca_repo = self 270 .csr 271 .ca_repository() 272 .map(uri::Rsync::to_string) 273 .unwrap_or_else(|| none.clone()); 274 let rpki_manifest = self 275 .csr 276 .rpki_manifest() 277 .map(uri::Rsync::to_string) 278 .unwrap_or_else(|| none.clone()); 279 280 write!( 281 f, 282 "class name '{}' limit '{}' csr for key '{}' rrdp notify '{}' ca repo '{}' mft '{}'", 283 self.class_name, self.limit, ki, rpki_notify, ca_repo, rpki_manifest 284 ) 285 } 286 } 287 288 //------------ IssuanceResponse ---------------------------------------------- 289 290 /// A Certificate Issuance Response equivalent to the one defined in 291 /// section 3.4.2 of RFC6492. 292 /// 293 /// Note that this is like a single EntitlementClass response, except that 294 /// it includes the one certificate which has just been issued only. 295 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 296 pub struct IssuanceResponse { 297 class_name: ResourceClassName, 298 issuer: SigningCert, 299 resource_set: ResourceSet, // resources allowed on a cert 300 not_after: Time, 301 issued: IssuedCert, 302 } 303 304 impl IssuanceResponse { new( class_name: ResourceClassName, issuer: SigningCert, resource_set: ResourceSet, not_after: Time, issued: IssuedCert, ) -> Self305 pub fn new( 306 class_name: ResourceClassName, 307 issuer: SigningCert, 308 resource_set: ResourceSet, // resources allowed on a cert 309 not_after: Time, 310 issued: IssuedCert, 311 ) -> Self { 312 IssuanceResponse { 313 class_name, 314 issuer, 315 resource_set, 316 not_after, 317 issued, 318 } 319 } 320 unwrap(self) -> (ResourceClassName, SigningCert, ResourceSet, IssuedCert)321 pub fn unwrap(self) -> (ResourceClassName, SigningCert, ResourceSet, IssuedCert) { 322 (self.class_name, self.issuer, self.resource_set, self.issued) 323 } 324 class_name(&self) -> &ResourceClassName325 pub fn class_name(&self) -> &ResourceClassName { 326 &self.class_name 327 } 328 issuer(&self) -> &SigningCert329 pub fn issuer(&self) -> &SigningCert { 330 &self.issuer 331 } 332 resource_set(&self) -> &ResourceSet333 pub fn resource_set(&self) -> &ResourceSet { 334 &self.resource_set 335 } 336 not_after(&self) -> Time337 pub fn not_after(&self) -> Time { 338 self.not_after 339 } 340 issued(&self) -> &IssuedCert341 pub fn issued(&self) -> &IssuedCert { 342 &self.issued 343 } 344 } 345 346 //------------ RequestResourceLimit ------------------------------------------ 347 348 /// The scope of resources that a child CA wants to have certified. By default 349 /// there are no limits, i.e. all the child wants all resources the parent is 350 /// willing to give. Only if some values are specified for certain resource 351 /// types will the scope be limited for that type only. Note that asking for 352 /// more than you are entitled to as a child, will anger a parent. In this case 353 /// the IssuanceRequest will be rejected. 354 /// 355 /// See: https://tools.ietf.org/html/rfc6492#section-3.4.1 356 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 357 pub struct RequestResourceLimit { 358 #[serde( 359 deserialize_with = "ext_serde::de_as_blocks_opt", 360 serialize_with = "ext_serde::ser_as_blocks_opt" 361 )] 362 asn: Option<AsBlocks>, 363 364 #[serde( 365 deserialize_with = "ext_serde::de_ip_blocks_4_opt", 366 serialize_with = "ext_serde::ser_ip_blocks_4_opt" 367 )] 368 v4: Option<IpBlocks>, 369 370 #[serde( 371 deserialize_with = "ext_serde::de_ip_blocks_6_opt", 372 serialize_with = "ext_serde::ser_ip_blocks_6_opt" 373 )] 374 v6: Option<IpBlocks>, 375 } 376 377 impl RequestResourceLimit { new() -> RequestResourceLimit378 pub fn new() -> RequestResourceLimit { 379 Self::default() 380 } 381 is_empty(&self) -> bool382 pub fn is_empty(&self) -> bool { 383 self.asn == None && self.v4 == None && self.v6 == None 384 } 385 with_asn(&mut self, asn: AsBlocks)386 pub fn with_asn(&mut self, asn: AsBlocks) { 387 self.asn = Some(asn); 388 } 389 with_ipv4(&mut self, ipv4: IpBlocks)390 pub fn with_ipv4(&mut self, ipv4: IpBlocks) { 391 self.v4 = Some(ipv4); 392 } 393 with_ipv6(&mut self, ipv6: IpBlocks)394 pub fn with_ipv6(&mut self, ipv6: IpBlocks) { 395 self.v6 = Some(ipv6); 396 } 397 asn(&self) -> Option<&AsBlocks>398 pub fn asn(&self) -> Option<&AsBlocks> { 399 self.asn.as_ref() 400 } 401 v4(&self) -> Option<&IpBlocks>402 pub fn v4(&self) -> Option<&IpBlocks> { 403 self.v4.as_ref() 404 } 405 v6(&self) -> Option<&IpBlocks>406 pub fn v6(&self) -> Option<&IpBlocks> { 407 self.v6.as_ref() 408 } 409 } 410 411 impl Default for RequestResourceLimit { default() -> Self412 fn default() -> Self { 413 RequestResourceLimit { 414 asn: None, 415 v4: None, 416 v6: None, 417 } 418 } 419 } 420 421 impl FromStr for RequestResourceLimit { 422 type Err = (); 423 from_str(s: &str) -> Result<Self, Self::Err>424 fn from_str(s: &str) -> Result<Self, Self::Err> { 425 let v4_lead = "v4 '"; 426 let v6_lead = "' v6 '"; 427 let asn_lead = "' asn '"; 428 429 if !s.starts_with(v4_lead) { 430 return Err(()); 431 } 432 433 if s.len() < v4_lead.len() + v6_lead.len() + asn_lead.len() + 1 { 434 return Err(()); 435 } 436 437 let v6_lead_start = s.find(v6_lead).ok_or(())?; 438 let asn_lead_start = s.find(asn_lead).ok_or(())?; 439 440 let v4_str = &s[v4_lead.len()..v6_lead_start]; 441 let v6_str = &s[v6_lead_start + v6_lead.len()..asn_lead_start]; 442 let asn_str = &s[asn_lead_start + asn_lead.len()..s.len() - 1]; 443 444 let v4 = if v4_str == "all" { 445 None 446 } else { 447 Some(IpBlocks::from_str(v4_str).map_err(|_| ())?) 448 }; 449 450 let v6 = if v6_str == "all" { 451 None 452 } else { 453 Some(IpBlocks::from_str(v6_str).map_err(|_| ())?) 454 }; 455 456 let asn = if asn_str == "all" { 457 None 458 } else { 459 Some(AsBlocks::from_str(asn_str).map_err(|_| ())?) 460 }; 461 Ok(RequestResourceLimit { asn, v4, v6 }) 462 } 463 } 464 465 impl fmt::Display for RequestResourceLimit { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result466 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 467 let all = "all".to_string(); 468 let v4_string = self 469 .v4 470 .as_ref() 471 .map(|blocks| IpBlocksForFamily::v4(blocks).to_string()) 472 .unwrap_or_else(|| all.clone()); 473 let v6_string = self 474 .v6 475 .as_ref() 476 .map(|blocks| IpBlocksForFamily::v6(blocks).to_string()) 477 .unwrap_or_else(|| all.clone()); 478 let asn_string = self.asn.as_ref().map(AsBlocks::to_string).unwrap_or_else(|| all); 479 480 write!(f, "v4 '{}' v6 '{}' asn '{}'", v4_string, v6_string, asn_string) 481 } 482 } 483 484 //------------ RevocationRequest --------------------------------------------- 485 486 /// This type represents a Certificate Revocation Request as 487 /// defined in section 3.5.1 of RFC6492. 488 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 489 pub struct RevocationRequest { 490 class_name: ResourceClassName, 491 key: KeyIdentifier, 492 } 493 494 impl fmt::Display for RevocationRequest { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result495 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 496 write!(f, "class name '{}' key '{}'", self.class_name, self.key) 497 } 498 } 499 500 impl RevocationRequest { new(class_name: ResourceClassName, key: KeyIdentifier) -> Self501 pub fn new(class_name: ResourceClassName, key: KeyIdentifier) -> Self { 502 RevocationRequest { class_name, key } 503 } 504 class_name(&self) -> &ResourceClassName505 pub fn class_name(&self) -> &ResourceClassName { 506 &self.class_name 507 } key(&self) -> &KeyIdentifier508 pub fn key(&self) -> &KeyIdentifier { 509 &self.key 510 } 511 unpack(self) -> (ResourceClassName, KeyIdentifier)512 pub fn unpack(self) -> (ResourceClassName, KeyIdentifier) { 513 (self.class_name, self.key) 514 } 515 } 516 517 //------------ RevocationResponse -------------------------------------------- 518 519 /// This type represents a Certificate Revocation Response as 520 /// defined in section 3.5.2 of RFC6492. 521 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 522 pub struct RevocationResponse { 523 class_name: ResourceClassName, 524 key: KeyIdentifier, 525 } 526 527 impl RevocationResponse { new(class_name: ResourceClassName, key: KeyIdentifier) -> Self528 pub fn new(class_name: ResourceClassName, key: KeyIdentifier) -> Self { 529 RevocationResponse { class_name, key } 530 } 531 unpack(self) -> (ResourceClassName, KeyIdentifier)532 pub fn unpack(self) -> (ResourceClassName, KeyIdentifier) { 533 (self.class_name, self.key) 534 } 535 class_name(&self) -> &ResourceClassName536 pub fn class_name(&self) -> &ResourceClassName { 537 &self.class_name 538 } key(&self) -> &KeyIdentifier539 pub fn key(&self) -> &KeyIdentifier { 540 &self.key 541 } 542 } 543 544 impl From<&RevocationRequest> for RevocationResponse { from(req: &RevocationRequest) -> Self545 fn from(req: &RevocationRequest) -> Self { 546 RevocationResponse { 547 class_name: req.class_name.clone(), 548 key: req.key, 549 } 550 } 551 } 552 553 impl From<RevocationRequest> for RevocationResponse { from(req: RevocationRequest) -> Self554 fn from(req: RevocationRequest) -> Self { 555 RevocationResponse { 556 class_name: req.class_name, 557 key: req.key, 558 } 559 } 560 } 561