webrtc_sdp/
anonymizer.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5extern crate url;
6use address::{Address, ExplicitlyTypedAddress};
7use std::collections::HashMap;
8use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
9use std::num::Wrapping;
10
11pub trait AnonymizingClone {
12    fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self;
13}
14
15pub trait ToBytesVec {
16    fn to_byte_vec(&self) -> Vec<u8>;
17}
18
19impl ToBytesVec for u64 {
20    fn to_byte_vec(&self) -> Vec<u8> {
21        let mut bytes = Vec::new();
22        let mut val = *self;
23        for _ in 0..8 {
24            bytes.push(val as u8);
25            val <<= 8;
26        }
27        bytes.reverse();
28        bytes
29    }
30}
31
32/*
33* Anonymizes SDP in a stateful fashion, such that a pre-anonymized value will
34* always be transformed into the same anonymized value within the context of
35* the anonymizer.
36* Stores the opaque state necessary for intelligent anonymization of SDP. This
37* state can be stored and reused during the offer-answer period, and it
38* will maintain a stable set of masked values.
39*/
40pub struct StatefulSdpAnonymizer {
41    ips: HashMap<IpAddr, IpAddr>,
42    ip_v4_inc: Wrapping<u32>,
43    ip_v6_inc: Wrapping<u128>,
44    host_names: AnonymizationStrMap,
45    ports: HashMap<u32, u32>,
46    port_inc: Wrapping<u32>,
47    origin_users: AnonymizationStrMap,
48    ice_passwords: AnonymizationStrMap,
49    ice_users: AnonymizationStrMap,
50    cert_finger_prints: HashMap<Vec<u8>, Vec<u8>>,
51    cert_finger_print_inc: Wrapping<u64>,
52    cnames: AnonymizationStrMap,
53}
54
55impl Default for StatefulSdpAnonymizer {
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61impl StatefulSdpAnonymizer {
62    pub fn new() -> Self {
63        StatefulSdpAnonymizer {
64            ips: HashMap::new(),
65            ip_v4_inc: Wrapping(0),
66            ip_v6_inc: Wrapping(0),
67            host_names: AnonymizationStrMap::new("fqdn-", 8),
68            ports: HashMap::new(),
69            port_inc: Wrapping(0),
70            origin_users: AnonymizationStrMap::new("origin-user-", 8),
71            ice_passwords: AnonymizationStrMap::new("ice-password-", 8),
72            ice_users: AnonymizationStrMap::new("ice-user-", 8),
73            cert_finger_prints: HashMap::new(),
74            cert_finger_print_inc: Wrapping(0),
75            cnames: AnonymizationStrMap::new("cname-", 8),
76        }
77    }
78
79    pub fn mask_host(&mut self, host: &str) -> String {
80        self.host_names.mask(host)
81    }
82
83    pub fn mask_ip(&mut self, addr: &IpAddr) -> IpAddr {
84        if let Some(address) = self.ips.get(addr) {
85            return *address;
86        }
87        let mapped = match addr {
88            IpAddr::V4(_) => {
89                self.ip_v4_inc += Wrapping(1);
90                IpAddr::V4(Ipv4Addr::from(self.ip_v4_inc.0))
91            }
92            IpAddr::V6(_) => {
93                self.ip_v6_inc += Wrapping(1);
94                IpAddr::V6(Ipv6Addr::from(self.ip_v6_inc.0))
95            }
96        };
97        self.ips.insert(*addr, mapped);
98        mapped
99    }
100
101    pub fn mask_address(&mut self, address: &Address) -> Address {
102        match address {
103            Address::Fqdn(host) => Address::Fqdn(self.mask_host(host)),
104            Address::Ip(ip) => Address::Ip(self.mask_ip(ip)),
105        }
106    }
107
108    pub fn mask_typed_address(
109        &mut self,
110        address: &ExplicitlyTypedAddress,
111    ) -> ExplicitlyTypedAddress {
112        match address {
113            ExplicitlyTypedAddress::Fqdn {
114                address_type,
115                domain,
116            } => ExplicitlyTypedAddress::Fqdn {
117                address_type: *address_type,
118                domain: self.mask_host(domain),
119            },
120            ExplicitlyTypedAddress::Ip(ip) => ExplicitlyTypedAddress::Ip(self.mask_ip(ip)),
121        }
122    }
123
124    pub fn mask_port(&mut self, port: u32) -> u32 {
125        if let Some(stored) = self.ports.get(&port) {
126            return *stored;
127        }
128        self.port_inc += Wrapping(1);
129        self.ports.insert(port, self.port_inc.0);
130        self.port_inc.0
131    }
132
133    pub fn mask_origin_user(&mut self, user: &str) -> String {
134        self.origin_users.mask(user)
135    }
136
137    pub fn mask_ice_password(&mut self, password: &str) -> String {
138        self.ice_passwords.mask(password)
139    }
140
141    pub fn mask_ice_user(&mut self, user: &str) -> String {
142        self.ice_users.mask(user)
143    }
144
145    pub fn mask_cert_finger_print(&mut self, finger_print: &[u8]) -> Vec<u8> {
146        if let Some(stored) = self.cert_finger_prints.get(finger_print) {
147            return stored.clone();
148        }
149        self.cert_finger_print_inc += Wrapping(1);
150        self.cert_finger_prints.insert(
151            finger_print.to_vec(),
152            self.cert_finger_print_inc.0.to_byte_vec(),
153        );
154        self.cert_finger_print_inc.0.to_byte_vec()
155    }
156
157    pub fn mask_cname(&mut self, cname: &str) -> String {
158        self.cnames.mask(cname)
159    }
160}
161
162struct AnonymizationStrMap {
163    map: HashMap<String, String>,
164    counter: Wrapping<u64>,
165    prefix: &'static str,
166    padding: usize,
167}
168
169impl AnonymizationStrMap {
170    pub fn new(prefix: &'static str, padding: usize) -> Self {
171        Self {
172            map: HashMap::new(),
173            counter: Wrapping(0),
174            prefix,
175            padding,
176        }
177    }
178
179    pub fn mask(&mut self, value: &str) -> String {
180        let key = value.to_owned();
181        if let Some(stored) = self.map.get(&key) {
182            return stored.clone();
183        }
184        self.counter += Wrapping(1);
185        let store = format!(
186            "{}{:0padding$}",
187            self.prefix,
188            self.counter.0,
189            padding = self.padding
190        );
191        self.map.insert(key, store.clone());
192        store
193    }
194}
195
196#[cfg(test)]
197#[path = "./anonymizer_tests.rs"]
198mod tests;