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