Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Crypto APIs to have a provider scheme #592

Closed
wants to merge 9 commits into from
5 changes: 3 additions & 2 deletions src/_internal_test_exports/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use std::time::Duration;
use std::time::Instant;

use crate::change::{SdpAnswer, SdpOffer};
use crate::crypto::KeyingMaterial;
use crate::crypto::SrtpProfile;
use crate::crypto::{CryptoProviderId, KeyingMaterial, SrtpProfile};
use crate::format::Codec;
use crate::packet::{DepacketizingBuffer, RtpMeta};
use crate::rtp_::{Frequency, MediaTime, RtpHeader};
Expand Down Expand Up @@ -49,12 +48,14 @@ pub fn rtp_header(data: &[u8]) -> Option<()> {
#[cfg(feature = "_internal_test_exports")]
pub fn rtp_packet(data: &[u8]) -> Option<()> {
use crate::Session;
let crypto_provider = CryptoProviderId::default().into();
let mut rng = Rng::new(data);

let config = random_config(&mut rng)?;

let mut session = Session::new(&config);
session.set_keying_material(
crypto_provider,
KeyingMaterial::new(rng.slice(16)?.to_vec()),
SrtpProfile::PassThrough,
rng.bool()?,
Expand Down
232 changes: 59 additions & 173 deletions src/crypto/dtls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,46 @@

use std::collections::VecDeque;
use std::fmt;
use std::panic::UnwindSafe;
use std::time::Instant;

use crate::net::DatagramSend;

use super::{CryptoError, Fingerprint, KeyingMaterial, SrtpProfile};
use super::{
CryptoError, CryptoProvider, CryptoProviderId, Fingerprint, KeyingMaterial, SrtpProfile,
};

// libWebRTC says "WebRTC" here when doing OpenSSL, for BoringSSL they seem
// to generate a random 8 characters.
// https://webrtc.googlesource.com/src/+/1568f1b1330f94494197696fe235094e6293b258/rtc_base/rtc_certificate_generator.cc#27
//
// Pion also sets this to "WebRTC", maybe for compatibility reasons.
// https://github.com/pion/webrtc/blob/eed2bb2d3b9f204f9de1cd7e1046ca5d652778d2/constants.go#L31
pub const DTLS_CERT_IDENTITY: &str = "WebRTC";
pub(crate) trait DtlsIdentity: fmt::Debug {
fn fingerprint(&self) -> Fingerprint;
fn create_context(&self) -> Result<Box<dyn DtlsContext>, CryptoError>;
fn crypto_provider(&self) -> CryptoProvider;
fn boxed_clone(&self) -> Box<dyn DtlsIdentity>;
}

pub(crate) trait DtlsContext: UnwindSafe + Send + Sync {
// Returns the crypto context.
fn crypto_provider(&self) -> CryptoProvider;

// Returns the local certificate fingerprint.
fn local_fingerprint(&self) -> Fingerprint;

// DTLS session management
fn set_active(&mut self, active: bool) -> ();

Check failure on line 29 in src/crypto/dtls.rs

View workflow job for this annotation

GitHub Actions / lint

unneeded unit return type
fn is_active(&self) -> Option<bool>;
fn is_connected(&self) -> bool;
fn handle_handshake(
&mut self,
out_events: &mut VecDeque<DtlsEvent>,
) -> Result<bool, CryptoError>;
fn handle_receive(
&mut self,
datagram: &[u8],
out_events: &mut VecDeque<DtlsEvent>,
) -> Result<(), CryptoError>;
fn poll_datagram(&mut self) -> Option<DatagramSend>;
fn poll_timeout(&mut self, now: Instant) -> Option<Instant>;
fn handle_input(&mut self, data: &[u8]) -> Result<(), CryptoError>;
}

/// Events arising from a [`Dtls`] instance.
pub enum DtlsEvent {
Expand All @@ -34,190 +61,49 @@
}

/// Certificate used for DTLS.
#[derive(Clone)]
pub struct DtlsCert(DtlsCertInner);
pub struct DtlsCert(Box<dyn DtlsIdentity>);

#[derive(Debug, Clone)]
enum DtlsCertInner {
#[cfg(feature = "openssl")]
OpenSsl(super::ossl::OsslDtlsCert),
#[cfg(feature = "wincrypto")]
WinCrypto(super::wincrypto::WinCryptoDtlsCert),
impl Clone for DtlsCert {
fn clone(&self) -> Self {
Self(self.0.boxed_clone())
}
}

impl DtlsCert {
/// Create a new DtlsCert using the given provider.
pub fn new(crypto_provider_id: CryptoProviderId) -> Self {
let crypto_provider: CryptoProvider = crypto_provider_id.into();
DtlsCert(crypto_provider.create_dtls_identity())
}

#[cfg(feature = "openssl")]
/// Create a new OpenSSL variant of the certificate.
pub fn new_openssl() -> Self {
let cert = super::ossl::OsslDtlsCert::new();
DtlsCert(DtlsCertInner::OpenSsl(cert))
}

#[cfg(feature = "wincrypto")]
/// Create a new Windows Crypto variant of the certificate.
pub fn new_wincrypto() -> Self {
let cert = super::wincrypto::WinCryptoDtlsCert::new();
DtlsCert(DtlsCertInner::WinCrypto(cert))
Self::new(super::CryptoProviderId::default())
}

/// Creates a fingerprint for this certificate.
///
/// Fingerprints are used to verify a remote peer's certificate.
pub fn fingerprint(&self) -> Fingerprint {
match &self.0 {
#[cfg(feature = "openssl")]
DtlsCertInner::OpenSsl(v) => v.fingerprint(),
#[cfg(feature = "wincrypto")]
DtlsCertInner::WinCrypto(v) => v.fingerprint(),
_ => unreachable!(),
}
}

pub(crate) fn create_dtls_impl(&self) -> Result<DtlsImpl, CryptoError> {
match &self.0 {
#[cfg(feature = "openssl")]
DtlsCertInner::OpenSsl(c) => Ok(DtlsImpl::OpenSsl(super::ossl::OsslDtlsImpl::new(
c.clone(),
)?)),
#[cfg(feature = "wincrypto")]
DtlsCertInner::WinCrypto(c) => Ok(DtlsImpl::WinCrypto(
super::wincrypto::WinCryptoDtls::new(c.clone())?,
)),
_ => unreachable!(),
}
}
}

impl fmt::Debug for DtlsCert {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
#[cfg(feature = "openssl")]
DtlsCertInner::OpenSsl(c) => c.fmt(f),
#[cfg(feature = "wincrypto")]
DtlsCertInner::WinCrypto(c) => c.fmt(f),
_ => unreachable!(),
}
self.0.fingerprint()
}
}

pub trait DtlsInner: Sized {
/// Set whether this instance is active or passive.
/// Creates a DTLS context using this certificate as the identity.
///
/// i.e. initiating the client hello or not. This must be called
/// exactly once before starting to handshake (I/O).
fn set_active(&mut self, active: bool);

/// Handle the handshake. Once this succeeds, it becomes a no-op.
fn handle_handshake(&mut self, o: &mut VecDeque<DtlsEvent>) -> Result<bool, CryptoError>;

/// If set_active, returns what was set.
fn is_active(&self) -> Option<bool>;

/// Handles an incoming DTLS datagrams.
fn handle_receive(&mut self, m: &[u8], o: &mut VecDeque<DtlsEvent>) -> Result<(), CryptoError>;

/// Poll for the next datagram to send.
fn poll_datagram(&mut self) -> Option<DatagramSend>;

/// Poll for next timeout. This is only used during DTLS handshake.
fn poll_timeout(&mut self, now: Instant) -> Option<Instant>;

/// Handling incoming data to be sent as DTLS datagrams.
fn handle_input(&mut self, data: &[u8]) -> Result<(), CryptoError>;

/// Whether the DTLS connection is established.
fn is_connected(&self) -> bool;
}

pub enum DtlsImpl {
#[cfg(feature = "openssl")]
OpenSsl(super::ossl::OsslDtlsImpl),
#[cfg(feature = "wincrypto")]
WinCrypto(super::wincrypto::WinCryptoDtls),
}

impl DtlsImpl {
pub fn set_active(&mut self, active: bool) {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.set_active(active),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.set_active(active),
_ => unreachable!(),
}
/// Multiple contexts may be created using the same identity.
pub(crate) fn create_context(&self) -> Result<Box<dyn DtlsContext>, CryptoError> {
self.0.create_context()
}

pub fn handle_handshake(&mut self, o: &mut VecDeque<DtlsEvent>) -> Result<bool, CryptoError> {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.handle_handshake(o),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.handle_handshake(o),
_ => unreachable!(),
}
}

pub fn is_active(&self) -> Option<bool> {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.is_active(),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.is_active(),
_ => unreachable!(),
}
}

pub fn handle_receive(
&mut self,
m: &[u8],
o: &mut VecDeque<DtlsEvent>,
) -> Result<(), CryptoError> {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.handle_receive(m, o),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.handle_receive(m, o),
_ => unreachable!(),
}
}

pub fn poll_datagram(&mut self) -> Option<DatagramSend> {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.poll_datagram(),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.poll_datagram(),
_ => unreachable!(),
}
}

pub fn poll_timeout(&mut self, now: Instant) -> Option<Instant> {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.poll_timeout(now),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.poll_timeout(now),
_ => unreachable!(),
}
}

pub fn handle_input(&mut self, data: &[u8]) -> Result<(), CryptoError> {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.handle_input(data),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.handle_input(data),
_ => unreachable!(),
}
/// Obtains the CryptoProvider that this Cert was built with.
pub(crate) fn crypto_provider(&self) -> CryptoProvider {
self.0.crypto_provider()
}
}

pub fn is_connected(&self) -> bool {
match self {
#[cfg(feature = "openssl")]
DtlsImpl::OpenSsl(i) => i.is_connected(),
#[cfg(feature = "wincrypto")]
DtlsImpl::WinCrypto(i) => i.is_connected(),
_ => unreachable!(),
}
impl fmt::Debug for DtlsCert {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
Loading
Loading