use std::fmt;
use std::time::SystemTime;
use openssl::asn1::{Asn1Integer, Asn1Time, Asn1Type};
use openssl::bn::BigNum;
use openssl::ec::{EcGroup, EcKey};
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::{PKey, Private};
use openssl::rsa::Rsa;
use openssl::x509::{X509, X509Name};
use super::CryptoError;
use super::OsslDtlsImpl;
const RSA_F4: u32 = 0x10001;
#[derive(Debug, Clone)]
pub struct OsslDtlsCert {
pub pkey: PKey<Private>,
pub x509: X509,
}
#[derive(Clone, Debug, Default)]
pub enum DtlsPKeyType {
Rsa2048,
#[default]
EcDsaP256,
EcDsaP384,
}
#[derive(Clone, Debug)]
pub struct DtlsCertOptions {
pub common_name: String,
pub pkey_type: DtlsPKeyType,
}
const DTLS_CERT_IDENTITY: &str = "WebRTC";
impl Default for DtlsCertOptions {
fn default() -> Self {
Self {
common_name: DTLS_CERT_IDENTITY.into(),
pkey_type: Default::default(),
}
}
}
impl OsslDtlsCert {
pub fn new(options: DtlsCertOptions) -> Self {
Self::self_signed(options).expect("create dtls cert")
}
fn self_signed(options: DtlsCertOptions) -> Result<Self, CryptoError> {
let f4 = BigNum::from_u32(RSA_F4)?;
let pkey = match options.pkey_type {
DtlsPKeyType::Rsa2048 => {
let key = Rsa::generate_with_e(2048, &f4)?;
PKey::from_rsa(key)?
}
DtlsPKeyType::EcDsaP256 => {
let nid = Nid::X9_62_PRIME256V1; let group = EcGroup::from_curve_name(nid)?;
let key = EcKey::generate(&group)?;
PKey::from_ec_key(key)?
}
DtlsPKeyType::EcDsaP384 => {
let nid = Nid::SECP384R1;
let group = EcGroup::from_curve_name(nid)?;
let key = EcKey::generate(&group)?;
PKey::from_ec_key(key)?
}
};
let mut x509b = X509::builder()?;
x509b.set_version(2)?;
let mut serial_buf = [0u8; 16];
openssl::rand::rand_bytes(&mut serial_buf)?;
let serial_bn = BigNum::from_slice(&serial_buf)?;
let serial = Asn1Integer::from_bn(&serial_bn)?;
x509b.set_serial_number(&serial)?;
let before = Asn1Time::from_unix(unix_time() - 3600)?;
x509b.set_not_before(&before)?;
let after = Asn1Time::days_from_now(7)?;
x509b.set_not_after(&after)?;
x509b.set_pubkey(&pkey)?;
let mut nameb = X509Name::builder()?;
nameb.append_entry_by_nid_with_type(
Nid::COMMONNAME,
options.common_name.as_str(),
Asn1Type::UTF8STRING,
)?;
let name = nameb.build();
x509b.set_subject_name(&name)?;
x509b.set_issuer_name(&name)?;
x509b.sign(&pkey, MessageDigest::sha256())?;
let x509 = x509b.build();
Ok(OsslDtlsCert { pkey, x509 })
}
pub fn fingerprint(&self) -> Fingerprint {
let digest: &[u8] = &self
.x509
.digest(MessageDigest::sha256())
.expect("digest to fingerprint");
Fingerprint {
hash_func: "sha-256".into(),
bytes: digest.to_vec(),
}
}
pub fn new_dtls_impl(&self) -> Result<OsslDtlsImpl, CryptoError> {
OsslDtlsImpl::new(self.clone())
}
pub fn new_dtls_impl_with_groups(
&self,
groups_list: &str,
) -> Result<OsslDtlsImpl, CryptoError> {
OsslDtlsImpl::new_with_groups(self.clone(), Some(groups_list))
}
}
#[derive(Debug)]
pub struct Fingerprint {
pub hash_func: String,
pub bytes: Vec<u8>,
}
impl fmt::Display for Fingerprint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ", self.hash_func)?;
for (i, b) in self.bytes.iter().enumerate() {
if i > 0 {
write!(f, ":")?;
}
write!(f, "{:02X}", b)?;
}
Ok(())
}
}
pub fn unix_time() -> libc::time_t {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as libc::time_t
}