Skip to main content

clia_turn/auth/
mod.rs

1#[cfg(test)]
2mod auth_test;
3
4use std::net::SocketAddr;
5use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7#[cfg(feature = "async-auth")]
8use async_trait::async_trait;
9use base64::prelude::BASE64_STANDARD;
10use base64::Engine;
11use md5::{Digest, Md5};
12use ring::hmac;
13
14use crate::error::*;
15
16#[cfg(not(feature = "async-auth"))]
17pub trait AuthHandler {
18    fn auth_handle(&self, username: &str, realm: &str, src_addr: SocketAddr) -> Result<Vec<u8>>;
19}
20
21#[cfg(feature = "async-auth")]
22#[async_trait]
23pub trait AuthHandler {
24    async fn auth_handle(
25        &self,
26        username: &str,
27        realm: &str,
28        src_addr: SocketAddr,
29    ) -> Result<Vec<u8>>;
30}
31
32/// `generate_long_term_credentials()` can be used to create credentials valid for `duration` time/
33pub fn generate_long_term_credentials(
34    shared_secret: &str,
35    duration: Duration,
36) -> Result<(String, String)> {
37    let t = SystemTime::now().duration_since(UNIX_EPOCH)? + duration;
38    let username = format!("{}", t.as_secs());
39    let password = long_term_credentials(&username, shared_secret);
40    Ok((username, password))
41}
42
43fn long_term_credentials(username: &str, shared_secret: &str) -> String {
44    let mac = hmac::Key::new(
45        hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
46        shared_secret.as_bytes(),
47    );
48    let password = hmac::sign(&mac, username.as_bytes()).as_ref().to_vec();
49    BASE64_STANDARD.encode(password)
50}
51
52/// A convenience function to easily generate keys in the format used by [`AuthHandler`].
53pub fn generate_auth_key(username: &str, realm: &str, password: &str) -> Vec<u8> {
54    let s = format!("{username}:{realm}:{password}");
55
56    let mut h = Md5::new();
57    h.update(s.as_bytes());
58    h.finalize().as_slice().to_vec()
59}
60
61pub struct LongTermAuthHandler {
62    shared_secret: String,
63}
64
65#[cfg(not(feature = "async-auth"))]
66impl AuthHandler for LongTermAuthHandler {
67    fn auth_handle(&self, username: &str, realm: &str, src_addr: SocketAddr) -> Result<Vec<u8>> {
68        log::trace!(
69            "Authentication username={} realm={} src_addr={}",
70            username,
71            realm,
72            src_addr
73        );
74
75        let t = Duration::from_secs(username.parse::<u64>()?);
76        if t < SystemTime::now().duration_since(UNIX_EPOCH)? {
77            return Err(Error::Other(format!(
78                "Expired time-windowed username {username}"
79            )));
80        }
81
82        let password = long_term_credentials(username, &self.shared_secret);
83        Ok(generate_auth_key(username, realm, &password))
84    }
85}
86#[cfg(feature = "async-auth")]
87#[async_trait]
88impl AuthHandler for LongTermAuthHandler {
89    async fn auth_handle(
90        &self,
91        username: &str,
92        realm: &str,
93        src_addr: SocketAddr,
94    ) -> Result<Vec<u8>> {
95        log::trace!(
96            "Authentication username={} realm={} src_addr={}",
97            username,
98            realm,
99            src_addr
100        );
101
102        let t = Duration::from_secs(username.parse::<u64>()?);
103        if t < SystemTime::now().duration_since(UNIX_EPOCH)? {
104            return Err(Error::Other(format!(
105                "Expired time-windowed username {username}"
106            )));
107        }
108
109        let password = long_term_credentials(username, &self.shared_secret);
110        Ok(generate_auth_key(username, realm, &password))
111    }
112}
113
114impl LongTermAuthHandler {
115    /// https://tools.ietf.org/search/rfc5389#section-10.2
116    pub fn new(shared_secret: String) -> Self {
117        LongTermAuthHandler { shared_secret }
118    }
119}