1 //! Autonomous System Provider Authorization 2 //! 3 //! This is still being discussed in the IETF. No RFC just yet. 4 //! See the following drafts: 5 //! https://datatracker.ietf.org/doc/draft-ietf-sidrops-aspa-profile/ 6 //! https://datatracker.ietf.org/doc/draft-ietf-sidrops-aspa-verification/ 7 8 use std::fmt; 9 use std::str::FromStr; 10 use bcder::{decode, encode}; 11 use bcder::{Captured, Mode, Oid, Tag}; 12 use bcder::encode::Values; 13 use super::oid; 14 use super::cert::{Cert, ResourceCert}; 15 use super::crypto::{Signer, SigningError}; 16 use super::resources::{ 17 AddressFamily, AsBlock, AsBlocks, AsBlocksBuilder, AsId, AsResources 18 }; 19 use super::sigobj::{SignedObject, SignedObjectBuilder}; 20 use super::x509::ValidationError; 21 22 23 //------------ Aspa ---------------------------------------------------------- 24 #[derive(Clone, Debug)] 25 pub struct Aspa { 26 signed: SignedObject, 27 content: AsProviderAttestation, 28 } 29 30 impl Aspa { decode<S: decode::Source>( source: S, strict: bool ) -> Result<Self, S::Err>31 pub fn decode<S: decode::Source>( 32 source: S, 33 strict: bool 34 ) -> Result<Self, S::Err> { 35 let signed = SignedObject::decode(source, strict)?; 36 if signed.content_type().ne(&oid::CT_ASPA) { 37 return Err(decode::Malformed.into()) 38 } 39 let content = signed.decode_content(|cons| { 40 AsProviderAttestation::take_from(cons) 41 })?; 42 Ok(Aspa { signed, content }) 43 } 44 process<F>( mut self, issuer: &ResourceCert, strict: bool, check_crl: F ) -> Result<(ResourceCert, AsProviderAttestation), ValidationError> where F: FnOnce(&Cert) -> Result<(), ValidationError>45 pub fn process<F>( 46 mut self, 47 issuer: &ResourceCert, 48 strict: bool, 49 check_crl: F 50 ) -> Result<(ResourceCert, AsProviderAttestation), ValidationError> 51 where F: FnOnce(&Cert) -> Result<(), ValidationError> { 52 let cert = self.signed.validate(issuer, strict)?; 53 check_crl(cert.as_ref())?; 54 self.content.validate(&cert)?; 55 Ok((cert, self.content)) 56 } 57 58 /// Returns a value encoder for a reference to an ASPA. encode_ref(&self) -> impl encode::Values + '_59 pub fn encode_ref(&self) -> impl encode::Values + '_ { 60 self.signed.encode_ref() 61 } 62 63 /// Returns a DER encoded Captured for this ASPA. to_captured(&self) -> Captured64 pub fn to_captured(&self) -> Captured { 65 self.encode_ref().to_captured(Mode::Der) 66 } 67 68 /// Returns a reference to the EE certificate of this ROA. cert(&self) -> &Cert69 pub fn cert(&self) -> &Cert { 70 self.signed.cert() 71 } 72 } 73 74 75 //--- Deserialize and Serialize 76 77 #[cfg(feature = "serde")] 78 impl serde::Serialize for Aspa { serialize<S: serde::Serializer>( &self, serializer: S ) -> Result<S::Ok, S::Error>79 fn serialize<S: serde::Serializer>( 80 &self, serializer: S 81 ) -> Result<S::Ok, S::Error> { 82 let bytes = self.to_captured().into_bytes(); 83 let b64 = base64::encode(&bytes); 84 b64.serialize(serializer) 85 } 86 } 87 88 #[cfg(feature = "serde")] 89 impl<'de> serde::Deserialize<'de> for Aspa { deserialize<D: serde::Deserializer<'de>>( deserializer: D ) -> Result<Self, D::Error>90 fn deserialize<D: serde::Deserializer<'de>>( 91 deserializer: D 92 ) -> Result<Self, D::Error> { 93 use serde::de; 94 95 let string = String::deserialize(deserializer)?; 96 let decoded = base64::decode(&string).map_err(de::Error::custom)?; 97 let bytes = bytes::Bytes::from(decoded); 98 Aspa::decode(bytes, true).map_err(de::Error::custom) 99 } 100 } 101 102 103 //------------ AsProviderAttestation ----------------------------------------- 104 105 #[derive(Clone, Debug)] 106 pub struct AsProviderAttestation { 107 customer_as: AsId, 108 provider_as_set: ProviderAsSet, 109 } 110 111 impl AsProviderAttestation { take_from<S: decode::Source>( cons: &mut decode::Constructed<S> ) -> Result<Self, S::Err>112 fn take_from<S: decode::Source>( 113 cons: &mut decode::Constructed<S> 114 ) -> Result<Self, S::Err> { 115 // version [0] EXPLICIT INTEGER DEFAULT 0 116 cons.take_opt_constructed_if(Tag::CTX_0, |c| c.skip_u8_if(0))?; 117 118 cons.take_sequence(|cons| { 119 let customer_as = AsId::take_from(cons)?; 120 let provider_as_set = ProviderAsSet::take_from(cons)?; 121 122 Ok(AsProviderAttestation { 123 customer_as, 124 provider_as_set, 125 }) 126 }) 127 } 128 validate( &mut self, cert: &ResourceCert ) -> Result<(), ValidationError>129 fn validate( 130 &mut self, 131 cert: &ResourceCert 132 ) -> Result<(), ValidationError> { 133 if !cert.as_resources().contains(&self.as_blocks()) { 134 return Err(ValidationError); 135 } 136 Ok(()) 137 } 138 as_blocks(&self) -> AsBlocks139 fn as_blocks(&self) -> AsBlocks { 140 let mut builder = AsBlocksBuilder::new(); 141 let block = AsBlock::Id(self.customer_as); 142 builder.push(block); 143 144 builder.finalize() 145 } 146 as_resources(&self) -> AsResources147 pub fn as_resources(&self) -> AsResources { 148 AsResources::blocks(self.as_blocks()) 149 } 150 encode_ref(&self) -> impl encode::Values + '_151 pub fn encode_ref(&self) -> impl encode::Values + '_ { 152 encode::sequence(( 153 // version is DEFAULT 154 self.customer_as.encode(), 155 &self.provider_as_set.0, 156 )) 157 } 158 } 159 160 161 //------------ ProviderAsSet ------------------------------------------------- 162 163 #[derive(Clone, Debug)] 164 pub struct ProviderAsSet(Captured); 165 166 impl ProviderAsSet { iter(&self) -> ProviderAsIter167 pub fn iter(&self) -> ProviderAsIter { 168 ProviderAsIter(self.0.as_ref()) 169 } 170 take_from<S: decode::Source>( cons: &mut decode::Constructed<S> ) -> Result<Self, S::Err>171 fn take_from<S: decode::Source>( 172 cons: &mut decode::Constructed<S> 173 ) -> Result<Self, S::Err> { 174 cons.take_sequence(|cons| { 175 cons.capture(|cons| { 176 let mut last: Option<AsId> = None; 177 let mut entries = true; 178 while entries { 179 if let Some(provider_as) = ProviderAs::take_opt_from( 180 cons 181 )? { 182 let current_as_id = provider_as.provider(); 183 if let Some(last_as_id) = last { 184 if last_as_id >= current_as_id { 185 return Err(decode::Malformed.into()); 186 } 187 } 188 last = Some(provider_as.provider()); 189 } else { 190 entries = false; 191 } 192 } 193 194 Ok(()) 195 }) 196 }).map(ProviderAsSet) 197 } 198 } 199 200 201 //------------ ProviderAsIter ------------------------------------------------ 202 203 #[derive(Clone, Debug)] 204 pub struct ProviderAsIter<'a>(&'a [u8]); 205 206 impl<'a> Iterator for ProviderAsIter<'a> { 207 type Item = ProviderAs; 208 next(&mut self) -> Option<Self::Item>209 fn next(&mut self) -> Option<Self::Item> { 210 if self.0.is_empty() { 211 None 212 } 213 else { 214 Mode::Der.decode(&mut self.0, |cons| { 215 ProviderAs::take_opt_from(cons) 216 }).unwrap() 217 } 218 } 219 } 220 221 222 //------------ AspaProvider ---------------------------------------------- 223 224 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 225 pub struct ProviderAs { 226 provider: AsId, 227 afi_limit: Option<AddressFamily>, 228 } 229 230 impl ProviderAs { new(provider: AsId) -> Self231 pub fn new(provider: AsId) -> Self { 232 ProviderAs { provider, afi_limit: None } 233 } 234 new_v4(provider: AsId) -> Self235 pub fn new_v4(provider: AsId) -> Self { 236 ProviderAs { provider, afi_limit: Some(AddressFamily::Ipv4) } 237 } 238 new_v6(provider: AsId) -> Self239 pub fn new_v6(provider: AsId) -> Self { 240 ProviderAs { provider, afi_limit: Some(AddressFamily::Ipv6) } 241 } 242 provider(&self) -> AsId243 pub fn provider(&self) -> AsId { 244 self.provider 245 } 246 afi_limit(&self) -> Option<AddressFamily>247 pub fn afi_limit(&self) -> Option<AddressFamily> { 248 self.afi_limit 249 } 250 251 } 252 253 impl ProviderAs { 254 // 255 // providerAS ::= SEQUENCE { 256 // providerASID ::= ASID, 257 // afiLimit ::= OCTET STRING (SIZE (2)) OPTIONAL 258 // } 259 // 260 // ASID ::= INTEGER 261 262 /// Takes an optional ProviderAS from the beginning of an encoded value. take_opt_from<S: decode::Source>( cons: &mut decode::Constructed<S> ) -> Result<Option<Self>, S::Err>263 pub fn take_opt_from<S: decode::Source>( 264 cons: &mut decode::Constructed<S> 265 ) -> Result<Option<Self>, S::Err> { 266 cons.take_opt_sequence(|cons|{ 267 let provider = AsId::take_from(cons)?; 268 let afi_limit = AddressFamily::take_opt_from(cons)?; 269 Ok(ProviderAs { provider, afi_limit }) 270 }) 271 } 272 273 /// Skips over a ProviderAs if it is present. skip_opt_in<S: decode::Source>( cons: &mut decode::Constructed<S> ) -> Result<Option<()>, S::Err>274 pub fn skip_opt_in<S: decode::Source>( 275 cons: &mut decode::Constructed<S> 276 ) -> Result<Option<()>, S::Err> { 277 Self::take_opt_from(cons).map(|opt| opt.map(|_| ())) 278 } 279 encode(self) -> impl encode::Values280 pub fn encode(self) -> impl encode::Values { 281 encode::sequence(( 282 self.provider.encode(), 283 self.afi_limit.map(|v| v.encode()) 284 )) 285 } 286 } 287 288 289 //--- FromStr 290 291 impl FromStr for ProviderAs { 292 type Err = <AsId as FromStr>::Err; 293 from_str(s: &str) -> Result<Self, Self::Err>294 fn from_str(s: &str) -> Result<Self, Self::Err> { 295 // Possible options: 296 // AS# 297 // AS#(v4) 298 // AS#(v6) 299 if let Some(as_str) = s.strip_suffix("(v4)") { 300 Ok(ProviderAs::new_v4(AsId::from_str(as_str)?)) 301 } 302 else if let Some(as_str) = s.strip_suffix("(v6)") { 303 Ok(ProviderAs::new_v6(AsId::from_str(as_str)?)) 304 } 305 else { 306 Ok(ProviderAs::new(AsId::from_str(s)?)) 307 } 308 } 309 } 310 311 312 //--- Display 313 314 impl fmt::Display for ProviderAs { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 316 match &self.afi_limit { 317 None => write!(f, "{}", self.provider), 318 Some(family) => { 319 let fam_str = match &family { 320 AddressFamily::Ipv4 => "v4", 321 AddressFamily::Ipv6 => "v6", 322 }; 323 write!(f, "{}({})", self.provider, fam_str) 324 } 325 } 326 } 327 } 328 329 330 //--- Deserialize and Serialize 331 332 #[cfg(feature = "serde")] 333 impl serde::Serialize for ProviderAs { serialize<S: serde::Serializer>( &self, serializer: S ) -> Result<S::Ok, S::Error>334 fn serialize<S: serde::Serializer>( 335 &self, serializer: S 336 ) -> Result<S::Ok, S::Error> { 337 self.to_string().serialize(serializer) 338 } 339 } 340 341 #[cfg(feature = "serde")] 342 impl<'de> serde::Deserialize<'de> for ProviderAs { deserialize<D: serde::Deserializer<'de>>( deserializer: D ) -> Result<Self, D::Error>343 fn deserialize<D: serde::Deserializer<'de>>( 344 deserializer: D 345 ) -> Result<Self, D::Error> { 346 use serde::de; 347 348 let string = String::deserialize(deserializer)?; 349 ProviderAs::from_str(&string).map_err(de::Error::custom) 350 } 351 } 352 353 354 //------------ AspaBuilder --------------------------------------------------- 355 356 pub struct AspaBuilder { 357 customer_as: AsId, 358 providers: Vec<ProviderAs> 359 } 360 361 impl AspaBuilder { new( customer_as: AsId, providers: Vec<ProviderAs> ) -> Result<Self, DuplicateProviderAs>362 pub fn new( 363 customer_as: AsId, 364 providers: Vec<ProviderAs> 365 ) -> Result<Self, DuplicateProviderAs> { 366 let mut builder = AspaBuilder { 367 customer_as, 368 providers, 369 }; 370 builder.sort_and_verify_providers()?; 371 Ok(builder) 372 } 373 empty(customer_as: AsId) -> Self374 pub fn empty(customer_as: AsId) -> Self { 375 AspaBuilder { 376 customer_as, 377 providers: vec![], 378 } 379 } 380 add_provider( &mut self, provider: ProviderAs ) -> Result<(), DuplicateProviderAs>381 pub fn add_provider( 382 &mut self, provider: ProviderAs 383 ) -> Result<(), DuplicateProviderAs> { 384 self.providers.push(provider); 385 self.sort_and_verify_providers() 386 } 387 sort_and_verify_providers( &mut self ) -> Result<(), DuplicateProviderAs>388 fn sort_and_verify_providers( 389 &mut self 390 ) -> Result<(), DuplicateProviderAs> { 391 // sort and verify if there are any duplicates 392 if self.providers.len() > 1 { 393 self.providers.sort_by_key(|p| p.provider()); 394 let mut last = self.providers.first().unwrap().provider(); 395 for i in 1..self.providers.len() { 396 let new = self.providers.get(i).unwrap().provider(); 397 if new == last { 398 return Err(DuplicateProviderAs); 399 } 400 last = new; 401 } 402 } 403 Ok(()) 404 } 405 into_attestation(self) -> AsProviderAttestation406 fn into_attestation(self) -> AsProviderAttestation { 407 let provider_as_set_captured = Captured::from_values( 408 Mode::Der, 409 encode::sequence( 410 encode::slice( 411 self.providers.as_slice(), 412 |prov| prov.encode() 413 ) 414 ) 415 ); 416 417 let provider_as_set = ProviderAsSet(provider_as_set_captured); 418 419 AsProviderAttestation { 420 customer_as: self.customer_as, 421 provider_as_set, 422 } 423 } 424 425 /// Finalizes the builder into an ASPA. finalize<S: Signer>( self, mut sigobj: SignedObjectBuilder, signer: &S, issuer_key: &S::KeyId, ) -> Result<Aspa, SigningError<S::Error>>426 pub fn finalize<S: Signer>( 427 self, 428 mut sigobj: SignedObjectBuilder, 429 signer: &S, 430 issuer_key: &S::KeyId, 431 ) -> Result<Aspa, SigningError<S::Error>> { 432 let content = self.into_attestation(); 433 sigobj.set_as_resources(content.as_resources()); 434 435 let signed = sigobj.finalize( 436 Oid(oid::CT_ASPA.0.into()), 437 content.encode_ref().to_captured(Mode::Der).into_bytes(), 438 signer, 439 issuer_key, 440 )?; 441 Ok(Aspa { signed, content }) 442 } 443 } 444 445 446 //------------ DuplicateProviderAs ------------------------------------------- 447 448 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 449 pub struct DuplicateProviderAs; 450 451 impl fmt::Display for DuplicateProviderAs { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result452 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 453 f.write_str("provider as set contains duplicate") 454 } 455 } 456 457 impl std::error::Error for DuplicateProviderAs { } 458 459 460 //============ Test ========================================================== 461 462 #[cfg(all(test, feature = "softkeys"))] 463 mod signer_test { 464 use std::str::FromStr; 465 use crate::uri; 466 use crate::repository::cert::{KeyUsage, Overclaim, TbsCert}; 467 use crate::repository::crypto::{PublicKeyFormat, Signer}; 468 use crate::repository::crypto::softsigner::OpenSslSigner; 469 use crate::repository::resources::{AsId, Prefix}; 470 use crate::repository::tal::TalInfo; 471 use crate::repository::x509::Validity; 472 use super::*; 473 474 make_aspa( customer_as: AsId, mut providers: Vec<ProviderAs>, ) -> Aspa475 fn make_aspa( 476 customer_as: AsId, 477 mut providers: Vec<ProviderAs>, 478 ) -> Aspa { 479 let signer = OpenSslSigner::new(); 480 481 let issuer_key = signer.create_key(PublicKeyFormat::Rsa).unwrap(); 482 let issuer_uri = uri::Rsync::from_str( 483 "rsync://example.com/parent/ca.cer" 484 ).unwrap(); 485 let crl_uri = uri::Rsync::from_str( 486 "rsync://example.com/ca/ca.crl" 487 ).unwrap(); 488 let asa_uri = uri::Rsync::from_str( 489 "rsync://example.com/ca/asa.asa" 490 ).unwrap(); 491 492 let issuer_cert = { 493 let repo_uri = uri::Rsync::from_str( 494 "rsync://example.com/ca/" 495 ).unwrap(); 496 let mft_uri = uri::Rsync::from_str( 497 "rsync://example.com/ca/ca.mft" 498 ).unwrap(); 499 500 let pubkey = signer.get_key_info(&issuer_key).unwrap(); 501 502 let mut cert = TbsCert::new( 503 12u64.into(), 504 pubkey.to_subject_name(), 505 Validity::from_secs(86400), 506 None, 507 pubkey, 508 KeyUsage::Ca, 509 Overclaim::Refuse, 510 ); 511 cert.set_basic_ca(Some(true)); 512 cert.set_ca_repository(Some(repo_uri)); 513 cert.set_rpki_manifest(Some(mft_uri)); 514 cert.build_v4_resource_blocks(|b| b.push(Prefix::new(0, 0))); 515 cert.build_v6_resource_blocks(|b| b.push(Prefix::new(0, 0))); 516 cert.build_as_resource_blocks(|b| b.push((AsId::MIN, AsId::MAX))); 517 let cert = cert.into_cert(&signer, &issuer_key).unwrap(); 518 519 cert.validate_ta( 520 TalInfo::from_name("foo".into()).into_arc(), true 521 ).unwrap() 522 523 524 }; 525 526 let mut aspa = AspaBuilder::empty(customer_as); 527 528 for provider in &providers { 529 aspa.add_provider(*provider).unwrap(); 530 } 531 532 let aspa = aspa.finalize( 533 SignedObjectBuilder::new( 534 123_u64.into(), 535 Validity::from_secs(86400), 536 crl_uri, 537 issuer_uri, 538 asa_uri 539 ), 540 &signer, 541 &issuer_key 542 ).unwrap(); 543 544 let encoded = aspa.to_captured(); 545 let decoded = Aspa::decode(encoded.as_slice(), true).unwrap(); 546 547 assert_eq!(encoded.as_slice(), decoded.to_captured().as_slice()); 548 549 let (_, attestation) = decoded.process( 550 &issuer_cert, true, |_| Ok(()) 551 ).unwrap(); 552 553 assert_eq!(customer_as, attestation.customer_as); 554 let decoded_providers: Vec<_> = 555 attestation.provider_as_set.iter().collect(); 556 557 providers.sort_by_key(|p| p.provider()); 558 assert_eq!(providers, decoded_providers.as_slice()); 559 // Sorted vecs should match 560 561 aspa 562 } 563 564 #[test] encode_aspa()565 fn encode_aspa() { 566 let customer_as: AsId = 64496.into(); 567 let providers: Vec<ProviderAs> = vec![ 568 ProviderAs::new_v4(64498.into()), 569 ProviderAs::new(64497.into()), 570 ProviderAs::new_v6(64499.into()) 571 ]; 572 make_aspa(customer_as, providers); 573 } 574 575 #[test] 576 #[cfg(feature = "serde")] serde_aspa()577 fn serde_aspa() { 578 let customer_as: AsId = 64496.into(); 579 let providers: Vec<ProviderAs> = vec![ 580 ProviderAs::new_v4(64498.into()), 581 ProviderAs::new(64497.into()), 582 ProviderAs::new_v6(64499.into()) 583 ]; 584 let aspa = make_aspa(customer_as, providers); 585 586 let serialized = serde_json::to_string(&aspa).unwrap(); 587 let deserialized: Aspa = serde_json::from_str(&serialized).unwrap(); 588 589 assert_eq!( 590 aspa.to_captured().into_bytes(), 591 deserialized.to_captured().into_bytes() 592 ) 593 } 594 595 #[test] 596 #[cfg(feature = "serde")] serde_aspa_empty_providers()597 fn serde_aspa_empty_providers() { 598 let customer_as: AsId = 64496.into(); 599 let providers: Vec<ProviderAs> = vec![]; 600 let aspa = make_aspa(customer_as, providers); 601 602 let serialized = serde_json::to_string(&aspa).unwrap(); 603 let deserialized: Aspa = serde_json::from_str(&serialized).unwrap(); 604 605 assert_eq!( 606 aspa.to_captured().into_bytes(), 607 deserialized.to_captured().into_bytes() 608 ) 609 } 610 611 #[test] 612 #[cfg(feature = "serde")] serde_provider_as()613 fn serde_provider_as() { 614 let providers: Vec<ProviderAs> = vec![ 615 ProviderAs::new(64497.into()), 616 ProviderAs::new_v4(64498.into()), 617 ProviderAs::new_v6(64499.into()) 618 ]; 619 620 let serialized = serde_json::to_string(&providers).unwrap(); 621 let deserialized: Vec<_> = serde_json::from_str(&serialized).unwrap(); 622 623 assert_eq!( 624 providers, 625 deserialized 626 ) 627 } 628 } 629 630 631 //============ Specification Documentation =================================== 632 633 /// ASPA Specification. 634 /// 635 /// This is a documentation-only module. It summarizes the specification for 636 /// ASPAs, how they are parsed and constructed. 637 /// 638 /// A Autonomous System Provider Authorization (ASPA) is a [signed object] that 639 /// provides a means of verifying that a Customer Autonomous System holder has 640 /// authorized members of Provider set to be its upstream providers and for the 641 /// Providers to send prefixes received from the Customer Autonomous System in 642 /// all directions including providers and peers. 643 /// 644 /// which is defined as follows: 645 /// 646 /// ```txt 647 /// ct-ASPA CONTENT-TYPE ::= 648 /// { ASProviderAttestation IDENTIFIED BY id-ct-ASPA } 649 /// 650 /// id-ct-ASPA OBJECT IDENTIFIER ::= { id-ct TBD } 651 /// 652 /// ASProviderAttestation ::= SEQUENCE { 653 /// version [0] ASPAVersion DEFAULT v0, 654 /// customerASID ASID, 655 /// providers ProviderASSet, 656 /// } 657 /// 658 /// ASPAVersion ::= INTEGER { v0(0) } 659 /// 660 /// ASID ::= INTEGER 661 /// 662 /// providerASSET ::= SEQUENCE (SIZE(1..MAX)) OF ProviderAS } 663 /// 664 /// providerAS ::= SEQUENCE { 665 /// providerASID ::= ASID, 666 /// afiLimit ::= OCTET STRING (SIZE (2)) OPTIONAL 667 /// } 668 /// ``` 669 /// 670 /// The _version_ must be 0. The _afiLimit, if present, MUST be 671 /// either `"\0\x01"` for IPv4 or `"\0\x02"` for IPv6. 672 /// 673 /// [signed object]: ../../sigobj/spec/index.html 674 /// [ASPA Profile draft]: https://datatracker.ietf.org/doc/draft-ietf-sidrops-aspa-profile/ 675 pub mod spec {} 676