1 //! Route Origin Authorizations. 2 //! 3 //! For details, see RFC 6482. 4 5 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; 6 use bcder::{decode, encode}; 7 use bcder::{Captured, Mode, OctetString, Oid, Tag, xerr}; 8 use bcder::encode::{PrimitiveContent, Values}; 9 use super::oid; 10 use super::cert::{Cert, ResourceCert}; 11 use super::crypto::{Signer, SigningError}; 12 use super::resources::{Addr, AddressFamily, AsId, IpResources, Prefix}; 13 use super::sigobj::{SignedObject, SignedObjectBuilder}; 14 use super::x509::ValidationError; 15 16 17 //------------ Roa ----------------------------------------------------------- 18 19 #[derive(Clone, Debug)] 20 pub struct Roa { 21 signed: SignedObject, 22 content: RouteOriginAttestation, 23 } 24 25 impl Roa { decode<S: decode::Source>( source: S, strict: bool ) -> Result<Self, S::Err>26 pub fn decode<S: decode::Source>( 27 source: S, 28 strict: bool 29 ) -> Result<Self, S::Err> { 30 let signed = SignedObject::decode(source, strict)?; 31 if signed.content_type().ne(&oid::ROUTE_ORIGIN_AUTHZ) { 32 return Err(decode::Malformed.into()) 33 } 34 let content = signed.decode_content(|cons| { 35 RouteOriginAttestation::take_from(cons) 36 })?; 37 Ok(Roa { signed, content }) 38 } 39 process<F>( mut self, issuer: &ResourceCert, strict: bool, check_crl: F ) -> Result<(ResourceCert, RouteOriginAttestation), ValidationError> where F: FnOnce(&Cert) -> Result<(), ValidationError>40 pub fn process<F>( 41 mut self, 42 issuer: &ResourceCert, 43 strict: bool, 44 check_crl: F 45 ) -> Result<(ResourceCert, RouteOriginAttestation), ValidationError> 46 where F: FnOnce(&Cert) -> Result<(), ValidationError> { 47 let cert = self.signed.validate(issuer, strict)?; 48 check_crl(cert.as_ref())?; 49 self.content.validate(&cert)?; 50 Ok((cert, self.content)) 51 } 52 53 /// Returns a value encoder for a reference to a ROA. encode_ref(&self) -> impl encode::Values + '_54 pub fn encode_ref(&self) -> impl encode::Values + '_ { 55 self.signed.encode_ref() 56 } 57 58 /// Returns a DER encoded Captured for this ROA. to_captured(&self) -> Captured59 pub fn to_captured(&self) -> Captured { 60 self.encode_ref().to_captured(Mode::Der) 61 } 62 63 /// Returns a reference to the EE certificate of this ROA. cert(&self) -> &Cert64 pub fn cert(&self) -> &Cert { 65 self.signed.cert() 66 } 67 } 68 69 70 //--- Deserialize and Serialize 71 72 #[cfg(feature = "serde")] 73 impl serde::Serialize for Roa { serialize<S: serde::Serializer>( &self, serializer: S ) -> Result<S::Ok, S::Error>74 fn serialize<S: serde::Serializer>( 75 &self, serializer: S 76 ) -> Result<S::Ok, S::Error> { 77 let bytes = self.to_captured().into_bytes(); 78 let b64 = base64::encode(&bytes); 79 b64.serialize(serializer) 80 } 81 } 82 83 #[cfg(feature = "serde")] 84 impl<'de> serde::Deserialize<'de> for Roa { deserialize<D: serde::Deserializer<'de>>( deserializer: D ) -> Result<Self, D::Error>85 fn deserialize<D: serde::Deserializer<'de>>( 86 deserializer: D 87 ) -> Result<Self, D::Error> { 88 use serde::de; 89 90 let string = String::deserialize(deserializer)?; 91 let decoded = base64::decode(&string).map_err(de::Error::custom)?; 92 let bytes = bytes::Bytes::from(decoded); 93 Roa::decode(bytes, true).map_err(de::Error::custom) 94 } 95 } 96 97 98 //------------ RouteOriginAttestation ---------------------------------------- 99 100 #[derive(Clone, Debug)] 101 pub struct RouteOriginAttestation { 102 as_id: AsId, 103 v4_addrs: RoaIpAddresses, 104 v6_addrs: RoaIpAddresses, 105 } 106 107 impl RouteOriginAttestation { as_id(&self) -> AsId108 pub fn as_id(&self) -> AsId { 109 self.as_id 110 } 111 v4_addrs(&self) -> &RoaIpAddresses112 pub fn v4_addrs(&self) -> &RoaIpAddresses { 113 &self.v4_addrs 114 } 115 v6_addrs(&self) -> &RoaIpAddresses116 pub fn v6_addrs(&self) -> &RoaIpAddresses { 117 &self.v6_addrs 118 } 119 iter( &self ) -> impl Iterator<Item=FriendlyRoaIpAddress> + '_120 pub fn iter( 121 &self 122 ) -> impl Iterator<Item=FriendlyRoaIpAddress> + '_ { 123 self.v4_addrs.iter().map(|addr| FriendlyRoaIpAddress::new(addr, true)) 124 .chain( 125 self.v6_addrs.iter() 126 .map(|addr| FriendlyRoaIpAddress::new(addr, false)) 127 ) 128 } 129 } 130 131 impl RouteOriginAttestation { take_from<S: decode::Source>( cons: &mut decode::Constructed<S> ) -> Result<Self, S::Err>132 fn take_from<S: decode::Source>( 133 cons: &mut decode::Constructed<S> 134 ) -> Result<Self, S::Err> { 135 cons.take_sequence(|cons| { 136 // version [0] EXPLICIT INTEGER DEFAULT 0 137 cons.take_opt_constructed_if(Tag::CTX_0, |c| c.skip_u8_if(0))?; 138 let as_id = AsId::take_from(cons)?; 139 let mut v4 = None; 140 let mut v6 = None; 141 cons.take_sequence(|cons| { 142 while let Some(()) = cons.take_opt_sequence(|cons| { 143 match AddressFamily::take_from(cons)? { 144 AddressFamily::Ipv4 => { 145 if v4.is_some() { 146 xerr!(return Err(decode::Malformed.into())); 147 } 148 v4 = Some(RoaIpAddresses::take_from( 149 cons, AddressFamily::Ipv4 150 )?); 151 } 152 AddressFamily::Ipv6 => { 153 if v6.is_some() { 154 xerr!(return Err(decode::Malformed.into())); 155 } 156 v6 = Some(RoaIpAddresses::take_from( 157 cons, AddressFamily::Ipv6 158 )?); 159 } 160 } 161 Ok(()) 162 })? { } 163 Ok(()) 164 })?; 165 Ok(RouteOriginAttestation { 166 as_id, 167 v4_addrs: match v4 { 168 Some(addrs) => addrs, 169 None => RoaIpAddresses(Captured::empty(Mode::Der)) 170 }, 171 v6_addrs: match v6 { 172 Some(addrs) => addrs, 173 None => RoaIpAddresses(Captured::empty(Mode::Der)) 174 }, 175 }) 176 }) 177 } 178 validate( &mut self, cert: &ResourceCert ) -> Result<(), ValidationError>179 fn validate( 180 &mut self, 181 cert: &ResourceCert 182 ) -> Result<(), ValidationError> { 183 if !self.v4_addrs.is_empty() { 184 let blocks = cert.v4_resources(); 185 if blocks.is_empty() { 186 return Err(ValidationError) 187 } 188 for addr in self.v4_addrs.iter() { 189 if !blocks.contains_roa(&addr) { 190 return Err(ValidationError) 191 } 192 } 193 } 194 if !self.v6_addrs.is_empty() { 195 let blocks = cert.v6_resources(); 196 if blocks.is_empty() { 197 return Err(ValidationError) 198 } 199 for addr in self.v6_addrs.iter() { 200 if !blocks.contains_roa(&addr) { 201 return Err(ValidationError) 202 } 203 } 204 } 205 Ok(()) 206 } 207 encode_ref(&self) -> impl encode::Values + '_208 pub fn encode_ref(&self) -> impl encode::Values + '_ { 209 encode::sequence(( 210 // version is DEFAULT 211 self.as_id.encode(), 212 encode::sequence(( 213 self.v4_addrs.encode_ref_family([0x00, 0x01]), 214 self.v6_addrs.encode_ref_family([0x00, 0x02]), 215 )) 216 )) 217 } 218 219 } 220 221 222 //------------ RoaIpAddresses ------------------------------------------------ 223 224 #[derive(Clone, Debug)] 225 pub struct RoaIpAddresses(Captured); 226 227 impl RoaIpAddresses { take_from<S: decode::Source>( cons: &mut decode::Constructed<S>, family: AddressFamily, ) -> Result<Self, S::Err>228 fn take_from<S: decode::Source>( 229 cons: &mut decode::Constructed<S>, 230 family: AddressFamily, 231 ) -> Result<Self, S::Err> { 232 cons.take_sequence(|cons| { 233 cons.capture(|cons| { 234 while RoaIpAddress::skip_opt_in(cons, family)?.is_some() { } 235 Ok(()) 236 }) 237 }).map(RoaIpAddresses) 238 } 239 is_empty(&self) -> bool240 pub fn is_empty(&self) -> bool { 241 self.0.is_empty() 242 } 243 iter(&self) -> RoaIpAddressIter244 pub fn iter(&self) -> RoaIpAddressIter { 245 RoaIpAddressIter(self.0.as_ref()) 246 } 247 encode_ref_family( &self, family: [u8; 2] ) -> Option<impl encode::Values + '_>248 fn encode_ref_family( 249 &self, 250 family: [u8; 2] 251 ) -> Option<impl encode::Values + '_> { 252 if self.0.is_empty() { 253 None 254 } 255 else { 256 Some(encode::sequence(( 257 OctetString::encode_slice(family), 258 &self.0 259 ))) 260 } 261 } 262 263 } 264 265 266 //------------ RoaIpAddressIter ---------------------------------------------- 267 268 #[derive(Clone, Debug)] 269 pub struct RoaIpAddressIter<'a>(&'a [u8]); 270 271 impl<'a> Iterator for RoaIpAddressIter<'a> { 272 type Item = RoaIpAddress; 273 next(&mut self) -> Option<Self::Item>274 fn next(&mut self) -> Option<Self::Item> { 275 if self.0.is_empty() { 276 None 277 } 278 else { 279 Mode::Der.decode(&mut self.0, |cons| { 280 RoaIpAddress::take_opt_from_unchecked(cons) 281 }).unwrap() 282 } 283 } 284 } 285 286 287 //------------ RoaIpAddress -------------------------------------------------- 288 289 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 290 pub struct RoaIpAddress { 291 prefix: Prefix, 292 max_length: Option<u8> 293 } 294 295 impl RoaIpAddress { new(prefix: Prefix, max_length: Option<u8>) -> Self296 pub fn new(prefix: Prefix, max_length: Option<u8>) -> Self { 297 RoaIpAddress { prefix, max_length } 298 } 299 new_addr(addr: IpAddr, len: u8, max_len: Option<u8>) -> Self300 pub fn new_addr(addr: IpAddr, len: u8, max_len: Option<u8>) -> Self { 301 RoaIpAddress::new(Prefix::new(addr, len), max_len) 302 } 303 prefix(self) -> Prefix304 pub fn prefix(self) -> Prefix { 305 self.prefix 306 } 307 range(self) -> (Addr, Addr)308 pub fn range(self) -> (Addr, Addr) { 309 self.prefix.range() 310 } 311 } 312 313 impl RoaIpAddress { 314 // Section 3 of RFC 6482 defines ROAIPAddress as 315 // 316 // ```txt 317 // ROAIPAddress ::= SEQUENCE { 318 // address IPAddress, 319 // maxLength INTEGER OPTIONAL } 320 // 321 // IPAddress ::= BIT STRING 322 // ``` 323 // 324 // The address is the same as in section 2.1.1 of RFC 3779, that is, it 325 // is a bit string with all the bits of the prefix. 326 take_opt_from_unchecked<S: decode::Source>( cons: &mut decode::Constructed<S> ) -> Result<Option<Self>, S::Err>327 fn take_opt_from_unchecked<S: decode::Source>( 328 cons: &mut decode::Constructed<S> 329 ) -> Result<Option<Self>, S::Err> { 330 cons.take_opt_sequence(|cons| { 331 Ok(RoaIpAddress { 332 prefix: Prefix::take_from(cons)?, 333 max_length: cons.take_opt_u8()?, 334 }) 335 }) 336 } 337 338 /// Skips one address in a source. 339 /// 340 /// In order to check that the address is correctly formatted, this 341 /// function needs to know the address family of the address. skip_opt_in<S: decode::Source>( cons: &mut decode::Constructed<S>, family: AddressFamily, ) -> Result<Option<()>, S::Err>342 fn skip_opt_in<S: decode::Source>( 343 cons: &mut decode::Constructed<S>, 344 family: AddressFamily, 345 ) -> Result<Option<()>, S::Err> { 346 let addr = match Self::take_opt_from_unchecked(cons)? { 347 Some(addr) => addr, 348 None => return Ok(None) 349 }; 350 351 // Check that the prefix length fits the address family. 352 if addr.prefix.addr_len() > family.max_addr_len() { 353 return Err(decode::Malformed.into()) 354 } 355 356 // Check that a max length fits both family and prefix length. 357 if let Some(max_length) = addr.max_length { 358 if max_length > family.max_addr_len() 359 || max_length < addr.prefix.addr_len() 360 { 361 return Err(decode::Malformed.into()) 362 } 363 } 364 365 Ok(Some(())) 366 } 367 encode(&self) -> impl encode::Values368 fn encode(&self) -> impl encode::Values { 369 encode::sequence(( 370 self.prefix.encode(), 371 self.max_length.map(|v| v.encode()) 372 )) 373 } 374 } 375 376 377 //------------ FriendlyRoaIpAddress ------------------------------------------ 378 379 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 380 pub struct FriendlyRoaIpAddress { 381 addr: RoaIpAddress, 382 v4: bool 383 } 384 385 impl FriendlyRoaIpAddress { new(addr: RoaIpAddress, v4: bool) -> Self386 fn new(addr: RoaIpAddress, v4: bool) -> Self { 387 FriendlyRoaIpAddress { addr, v4 } 388 } 389 prefix(self) -> Prefix390 pub fn prefix(self) -> Prefix { 391 self.addr.prefix 392 } 393 is_v4(self) -> bool394 pub fn is_v4(self) -> bool { 395 self.v4 396 } 397 address(self) -> IpAddr398 pub fn address(self) -> IpAddr { 399 if self.v4 { 400 self.addr.prefix.to_v4().into() 401 } 402 else { 403 self.addr.prefix.to_v6().into() 404 } 405 } 406 address_length(self) -> u8407 pub fn address_length(self) -> u8 { 408 self.addr.prefix.addr_len() 409 } 410 max_length(self) -> u8411 pub fn max_length(self) -> u8 { 412 self.addr.max_length.unwrap_or_else(|| 413 self.addr.prefix.addr_len() 414 ) 415 } 416 } 417 418 419 //------------ RoaBuilder ---------------------------------------------------- 420 421 pub struct RoaBuilder { 422 as_id: AsId, 423 v4: RoaIpAddressesBuilder, 424 v6: RoaIpAddressesBuilder, 425 } 426 427 impl RoaBuilder { new(as_id: AsId) -> Self428 pub fn new(as_id: AsId) -> Self { 429 Self::with_addresses( 430 as_id, 431 RoaIpAddressesBuilder::new(), 432 RoaIpAddressesBuilder::new(), 433 ) 434 } 435 with_addresses( as_id: AsId, v4: RoaIpAddressesBuilder, v6: RoaIpAddressesBuilder ) -> Self436 pub fn with_addresses( 437 as_id: AsId, 438 v4: RoaIpAddressesBuilder, 439 v6: RoaIpAddressesBuilder 440 ) -> Self { 441 Self { as_id, v4, v6 } 442 } 443 as_id(&self) -> AsId444 pub fn as_id(&self) -> AsId { 445 self.as_id 446 } 447 set_as_id(&mut self, as_id: AsId)448 pub fn set_as_id(&mut self, as_id: AsId) { 449 self.as_id = as_id 450 } 451 v4(&self) -> &RoaIpAddressesBuilder452 pub fn v4(&self) -> &RoaIpAddressesBuilder { 453 &self.v4 454 } 455 v4_mut(&mut self) -> &mut RoaIpAddressesBuilder456 pub fn v4_mut(&mut self) -> &mut RoaIpAddressesBuilder { 457 &mut self.v4 458 } 459 v6(&self) -> &RoaIpAddressesBuilder460 pub fn v6(&self) -> &RoaIpAddressesBuilder { 461 &self.v6 462 } 463 v6_mut(&mut self) -> &mut RoaIpAddressesBuilder464 pub fn v6_mut(&mut self) -> &mut RoaIpAddressesBuilder { 465 &mut self.v6 466 } 467 push_addr( &mut self, addr: IpAddr, len: u8, max_len: Option<u8> )468 pub fn push_addr( 469 &mut self, addr: IpAddr, len: u8, max_len: Option<u8> 470 ) { 471 match addr { 472 IpAddr::V4(addr) => self.push_v4_addr(addr, len, max_len), 473 IpAddr::V6(addr) => self.push_v6_addr(addr, len, max_len) 474 } 475 } 476 push_v4(&mut self, addr: RoaIpAddress)477 pub fn push_v4(&mut self, addr: RoaIpAddress) { 478 self.v4_mut().push(addr) 479 } 480 push_v4_addr( &mut self, addr: Ipv4Addr, len: u8, max_len: Option<u8> )481 pub fn push_v4_addr( 482 &mut self, addr: Ipv4Addr, len: u8, max_len: Option<u8> 483 ) { 484 self.v4_mut().push_addr(IpAddr::V4(addr), len, max_len) 485 } 486 extend_v4_from_slice(&mut self, addrs: &[RoaIpAddress])487 pub fn extend_v4_from_slice(&mut self, addrs: &[RoaIpAddress]) { 488 self.v4_mut().extend_from_slice(addrs) 489 } 490 push_v6(&mut self, addr: RoaIpAddress)491 pub fn push_v6(&mut self, addr: RoaIpAddress) { 492 self.v6_mut().push(addr) 493 } 494 push_v6_addr( &mut self, addr: Ipv6Addr, len: u8, max_len: Option<u8> )495 pub fn push_v6_addr( 496 &mut self, addr: Ipv6Addr, len: u8, max_len: Option<u8> 497 ) { 498 self.v6_mut().push_addr(IpAddr::V6(addr), len, max_len) 499 } 500 extend_v6_from_slice(&mut self, addrs: &[RoaIpAddress])501 pub fn extend_v6_from_slice(&mut self, addrs: &[RoaIpAddress]) { 502 self.v6_mut().extend_from_slice(addrs) 503 } 504 to_attestation(&self) -> RouteOriginAttestation505 pub fn to_attestation(&self) -> RouteOriginAttestation { 506 RouteOriginAttestation { 507 as_id: self.as_id, 508 v4_addrs: self.v4.to_addresses(), 509 v6_addrs: self.v6.to_addresses(), 510 } 511 } 512 513 /// Finalizes the builder into a ROA. 514 /// 515 /// # Panic 516 /// 517 /// This method will panic if both the IPv4 and IPv6 addresses are empty 518 /// as that is not allowed and would lead to a malformed ROA. finalize<S: Signer>( self, mut sigobj: SignedObjectBuilder, signer: &S, issuer_key: &S::KeyId, ) -> Result<Roa, SigningError<S::Error>>519 pub fn finalize<S: Signer>( 520 self, 521 mut sigobj: SignedObjectBuilder, 522 signer: &S, 523 issuer_key: &S::KeyId, 524 ) -> Result<Roa, SigningError<S::Error>> { 525 let content = self.to_attestation(); 526 let v4 = self.v4.to_resources(); 527 let v6 = self.v6.to_resources(); 528 // There must be some resources in order to make a valid ROA. 529 assert!(v4.is_present() || v6.is_present()); 530 sigobj.set_v4_resources(v4); 531 sigobj.set_v6_resources(v6); 532 let signed = sigobj.finalize( 533 Oid(oid::ROUTE_ORIGIN_AUTHZ.0.into()), 534 content.encode_ref().to_captured(Mode::Der).into_bytes(), 535 signer, 536 issuer_key, 537 )?; 538 Ok(Roa { signed, content }) 539 } 540 } 541 542 543 //------------ RoaIpAddressesBuilder ----------------------------------------- 544 545 #[derive(Clone, Debug)] 546 pub struct RoaIpAddressesBuilder { 547 addrs: Vec<RoaIpAddress>, 548 } 549 550 impl RoaIpAddressesBuilder { new() -> Self551 pub fn new() -> Self { 552 RoaIpAddressesBuilder { 553 addrs: Vec::new() 554 } 555 } 556 push(&mut self, addr: RoaIpAddress)557 pub fn push(&mut self, addr: RoaIpAddress) { 558 self.addrs.push(addr) 559 } 560 push_addr(&mut self, addr: IpAddr, len: u8, max_len: Option<u8>)561 pub fn push_addr(&mut self, addr: IpAddr, len: u8, max_len: Option<u8>) { 562 self.push(RoaIpAddress::new_addr(addr, len, max_len)) 563 } 564 extend_from_slice(&mut self, addrs: &[RoaIpAddress])565 pub fn extend_from_slice(&mut self, addrs: &[RoaIpAddress]) { 566 self.addrs.extend_from_slice(addrs) 567 } 568 to_addresses(&self) -> RoaIpAddresses569 pub fn to_addresses(&self) -> RoaIpAddresses { 570 RoaIpAddresses( 571 if self.addrs.is_empty() { 572 Captured::empty(Mode::Der) 573 } 574 else { 575 Captured::from_values(Mode::Der, self.encode_ref()) 576 } 577 ) 578 } 579 to_resources(&self) -> IpResources580 pub fn to_resources(&self) -> IpResources { 581 IpResources::blocks( 582 self.addrs.iter().map(|addr| addr.prefix.into()).collect() 583 ) 584 } 585 encode_ref(&self) -> impl encode::Values + '_586 pub fn encode_ref(&self) -> impl encode::Values + '_ { 587 encode::sequence( 588 encode::slice(self.addrs.as_slice(), |v: &RoaIpAddress| v.encode()) 589 ) 590 } 591 } 592 593 impl Default for RoaIpAddressesBuilder { default() -> Self594 fn default() -> Self { 595 Self::new() 596 } 597 } 598 599 impl Extend<RoaIpAddress> for RoaIpAddressesBuilder { extend<T>(&mut self, iter: T) where T: IntoIterator<Item=RoaIpAddress>600 fn extend<T>(&mut self, iter: T) 601 where T: IntoIterator<Item=RoaIpAddress> { 602 self.addrs.extend(iter) 603 } 604 } 605 606 607 //============ Tests ========================================================= 608 609 #[cfg(test)] 610 mod test { 611 use super::*; 612 613 #[test] decode_roa()614 fn decode_roa() { 615 assert!( 616 Roa::decode( 617 include_bytes!("../../test-data/example-ripe.roa").as_ref(), 618 false 619 ).is_ok() 620 ) 621 } 622 623 #[test] decode_illegal_roas()624 fn decode_illegal_roas() { 625 assert!( 626 Roa::decode( 627 include_bytes!( 628 "../../test-data/prefix-len-overflow.roa" 629 ).as_ref(), 630 false 631 ).is_err() 632 ); 633 assert!( 634 Roa::decode( 635 include_bytes!("../../test-data/maxlen-overflow.roa").as_ref(), 636 false 637 ).is_err() 638 ); 639 assert!( 640 Roa::decode( 641 include_bytes!("../../test-data/maxlen-underflow.roa").as_ref(), 642 false 643 ).is_err() 644 ); 645 } 646 } 647 648 #[cfg(all(test, feature="softkeys"))] 649 mod signer_test { 650 use std::str::FromStr; 651 use bcder::encode::Values; 652 use crate::uri; 653 use crate::repository::cert::{KeyUsage, Overclaim, TbsCert}; 654 use crate::repository::crypto::{PublicKeyFormat, Signer}; 655 use crate::repository::crypto::softsigner::OpenSslSigner; 656 use crate::repository::resources::{AsId, Prefix}; 657 use crate::repository::tal::TalInfo; 658 use crate::repository::x509::Validity; 659 use super::*; 660 make_roa() -> Roa661 fn make_roa() -> Roa { 662 let signer = OpenSslSigner::new(); 663 let key = signer.create_key(PublicKeyFormat::Rsa).unwrap(); 664 let pubkey = signer.get_key_info(&key).unwrap(); 665 let uri = uri::Rsync::from_str("rsync://example.com/m/p").unwrap(); 666 667 let mut cert = TbsCert::new( 668 12u64.into(), pubkey.to_subject_name(), 669 Validity::from_secs(86400), None, pubkey, KeyUsage::Ca, 670 Overclaim::Trim 671 ); 672 cert.set_basic_ca(Some(true)); 673 cert.set_ca_repository(Some(uri.clone())); 674 cert.set_rpki_manifest(Some(uri.clone())); 675 cert.build_v4_resource_blocks(|b| b.push(Prefix::new(0, 0))); 676 cert.build_v6_resource_blocks(|b| b.push(Prefix::new(0, 0))); 677 cert.build_as_resource_blocks(|b| b.push((AsId::MIN, AsId::MAX))); 678 let cert = cert.into_cert(&signer, &key).unwrap(); 679 680 let mut roa = RoaBuilder::new(64496.into()); 681 roa.push_v4_addr(Ipv4Addr::new(192, 0, 2, 0), 24, None); 682 683 let roa = roa.finalize( 684 SignedObjectBuilder::new( 685 12u64.into(), Validity::from_secs(86400), uri.clone(), 686 uri.clone(), uri 687 ), 688 &signer, &key 689 ).unwrap(); 690 let roa = roa.encode_ref().to_captured(Mode::Der); 691 692 let roa = Roa::decode(roa.as_slice(), true).unwrap(); 693 let cert = cert.validate_ta( 694 TalInfo::from_name("foo".into()).into_arc(), true 695 ).unwrap(); 696 roa.clone().process(&cert, true, |_| Ok(())).unwrap(); 697 698 roa 699 } 700 701 #[test] encode_roa()702 fn encode_roa() { 703 make_roa(); 704 } 705 706 #[test] 707 #[cfg(feature = "serde")] serde_roa()708 fn serde_roa() { 709 let roa = make_roa(); 710 711 let serialized = serde_json::to_string(&roa).unwrap(); 712 let deser_roa: Roa = serde_json::from_str(&serialized).unwrap(); 713 714 assert_eq!( 715 roa.to_captured().into_bytes(), 716 deser_roa.to_captured().into_bytes() 717 ) 718 } 719 } 720 721 722 //============ Specification Documentation =================================== 723 724 /// ROA Specification. 725 /// 726 /// This is a documentation-only module. It summarizes the specification for 727 /// ROAs, how they are parsed and constructed. 728 /// 729 /// A Route Origin Authorization (ROA) is a [signed object] that assigns a 730 /// number of route prefixes to an AS number. It is specified in [RFC 6482]. 731 /// 732 /// The content of a ROA signed object is of type `RouteOriginAttestation` 733 /// which is defined as follows: 734 /// 735 /// ```txt 736 /// RouteOriginAttestation ::= SEQUENCE { 737 /// version [0] INTEGER DEFAULT 0, 738 /// asID ASID, 739 /// ipAddrBlocks SEQUENCE (SIZE(1..MAX)) OF ROAIPAddressFamily 740 /// } 741 /// 742 /// ASID ::= INTEGER 743 /// 744 /// ROAIPAddressFamily ::= SEQUENCE { 745 /// addressFamily OCTET STRING (SIZE (2..3)), 746 /// addresses SEQUENCE (SIZE (1..MAX)) OF ROAIPAddress 747 /// } 748 /// 749 /// ROAIPAddress ::= SEQUENCE { 750 /// address IPAddress, 751 /// maxLength INTEGER OPTIONAL 752 /// } 753 /// 754 /// IPAddress ::= BIT STRING 755 /// ``` 756 /// 757 /// The _version_ must be 0. The _addressFamily_ is identical to the field 758 /// used in RPKI certificate IP resources, i.e, `"\0\x01"` for IPv4 and 759 /// `"\0\x02"` for IPv6. 760 /// 761 /// [signed object]: ../../sigobj/spec/index.html 762 /// [RFC 6482]: https://tools.ietf.org/html/rfc6482 763 pub mod spec { } 764 765