#[cfg(feature = "dev-pki")]
pub mod dev;
use std::io::Read as _;
pub use x509_parser::certificate::X509Certificate;
use crate::Result;
pub const S2PKI_TLD: &str = "s2pki.net";
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum Authority {
R1,
T1,
T2,
S1,
S2,
S3,
S4,
F1,
B1,
B2,
B3,
B4,
}
impl Authority {
pub fn name(&self) -> String {
format!("{:?}", self)
}
}
impl TryFrom<&str> for Authority {
type Error = crate::Error;
fn try_from(name: &str) -> Result<Authority> {
Ok(match name.to_uppercase().as_str() {
"B1" => Authority::B1,
"B2" => Authority::B2,
"B3" => Authority::B3,
"B4" => Authority::B4,
"F1" => Authority::F1,
"R1" => Authority::R1,
"S1" => Authority::S1,
"S2" => Authority::S2,
"S3" => Authority::S3,
"S4" => Authority::S4,
"T1" => Authority::T1,
"T2" => Authority::T2,
_ => return Err(anyhow::anyhow!("Unknown authority name {}", name)),
})
}
}
#[derive(Clone, Debug)]
pub struct Certificate {
der: Vec<u8>,
}
impl Certificate {
pub fn try_from_der(der: &[u8]) -> Result<Self> {
use x509_parser::prelude::FromDer;
X509Certificate::from_der(der)?;
Ok(Self { der: der.to_vec() })
}
pub fn der(&self) -> &[u8] {
&self.der
}
pub fn certificate(&self) -> X509Certificate<'_> {
use x509_parser::prelude::FromDer;
X509Certificate::from_der(&self.der).unwrap().1
}
}
pub fn authority_information_access(authority: Authority) -> String {
format!("http://i.{}/{:?}/", S2PKI_TLD, authority).to_lowercase()
}
pub fn fetch_certificate(authority: Authority) -> Result<Certificate> {
let mut der = Vec::new();
ureq::get(&authority_information_access(authority))
.call()?
.into_reader()
.read_to_end(&mut der)?;
Certificate::try_from_der(&der)
}
#[cfg(all(test, feature = "network-tests"))]
mod test {
use super::*;
#[test]
fn urls() {
assert_eq!(
authority_information_access(Authority::R1),
"http://i.s2pki.net/r1/"
);
}
#[test]
fn r1() {
let r1 = fetch_certificate(Authority::R1).unwrap();
assert_eq!(r1.der(), include_bytes!("../data/r1.der"),);
}
#[test]
fn t1t2() {
let r1 = fetch_certificate(Authority::R1).unwrap();
let r1 = r1.certificate();
let r1_pubkey = Some(r1.public_key());
let t1 = fetch_certificate(Authority::T1).unwrap();
let t2 = fetch_certificate(Authority::T2).unwrap();
assert!(t1.certificate().verify_signature(r1_pubkey).is_ok());
assert!(t2.certificate().verify_signature(r1_pubkey).is_ok());
}
}