1 // decrypter.rs
2 //
3 // Copyright 2019 Jordan Petridis <jordan@centricular.com>
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to
7 // deal in the Software without restriction, including without limitation the
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 // sell copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 // IN THE SOFTWARE.
22 //
23 // SPDX-License-Identifier: MIT
24
25 use gst::glib;
26 use gst::prelude::*;
27
28 use std::sync::{Arc, Mutex};
29
30 use std::path::PathBuf;
31
32 use pretty_assertions::assert_eq;
33
34 use once_cell::sync::Lazy;
35 static SENDER_PUBLIC: Lazy<glib::Bytes> = Lazy::new(|| {
36 let public = [
37 66, 248, 199, 74, 216, 55, 228, 116, 52, 17, 147, 56, 65, 130, 134, 148, 157, 153, 235,
38 171, 179, 147, 120, 71, 100, 243, 133, 120, 160, 14, 111, 65,
39 ];
40 glib::Bytes::from_owned(public)
41 });
42 static RECEIVER_PRIVATE: Lazy<glib::Bytes> = Lazy::new(|| {
43 let secret = [
44 54, 221, 217, 54, 94, 235, 167, 2, 187, 249, 71, 31, 59, 27, 19, 166, 78, 236, 102, 48, 29,
45 142, 41, 189, 22, 146, 218, 69, 147, 165, 240, 235,
46 ];
47 glib::Bytes::from_owned(secret)
48 });
49
init()50 fn init() {
51 use std::sync::Once;
52 static INIT: Once = Once::new();
53
54 INIT.call_once(|| {
55 gst::init().unwrap();
56 gstsodium::plugin_register_static().unwrap();
57 });
58 }
59
60 #[test]
test_pipeline()61 fn test_pipeline() {
62 init();
63
64 let pipeline = gst::Pipeline::new(Some("sodium-decrypter-test"));
65
66 let input_path = {
67 let mut r = PathBuf::new();
68 r.push(env!("CARGO_MANIFEST_DIR"));
69 r.push("tests");
70 r.push("encrypted_sample");
71 r.set_extension("enc");
72 r
73 };
74
75 let filesrc = gst::ElementFactory::make("filesrc", None).unwrap();
76 filesrc
77 .set_property("location", &input_path.to_str().unwrap())
78 .expect("failed to set property");
79
80 let dec = gst::ElementFactory::make("sodiumdecrypter", None).unwrap();
81 dec.set_property("sender-key", &*SENDER_PUBLIC)
82 .expect("failed to set property");
83 dec.set_property("receiver-key", &*RECEIVER_PRIVATE)
84 .expect("failed to set property");
85
86 // the typefind element here is cause the decrypter only supports
87 // operating in pull mode bu the filesink wants push-mode.
88 let typefind = gst::ElementFactory::make("typefind", None).unwrap();
89 let sink = gst::ElementFactory::make("appsink", None).unwrap();
90
91 pipeline
92 .add_many(&[&filesrc, &dec, &typefind, &sink])
93 .expect("failed to add elements to the pipeline");
94 gst::Element::link_many(&[&filesrc, &dec, &typefind, &sink])
95 .expect("failed to link the elements");
96
97 let adapter = Arc::new(Mutex::new(gst_base::UniqueAdapter::new()));
98
99 let sink = sink.downcast::<gst_app::AppSink>().unwrap();
100 let adapter_clone = adapter.clone();
101 sink.set_callbacks(
102 gst_app::AppSinkCallbacks::builder()
103 // Add a handler to the "new-sample" signal.
104 .new_sample(move |appsink| {
105 // Pull the sample in question out of the appsink's buffer.
106 let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?;
107 let buffer = sample.buffer().ok_or(gst::FlowError::Error)?;
108
109 let mut adapter = adapter_clone.lock().unwrap();
110 adapter.push(buffer.to_owned());
111
112 Ok(gst::FlowSuccess::Ok)
113 })
114 .build(),
115 );
116
117 pipeline
118 .set_state(gst::State::Playing)
119 .expect("Unable to set the pipeline to the `Playing` state");
120
121 let bus = pipeline.bus().unwrap();
122 for msg in bus.iter_timed(gst::ClockTime::NONE) {
123 use gst::MessageView;
124 match msg.view() {
125 MessageView::Error(err) => {
126 eprintln!(
127 "Error received from element {:?}: {}",
128 err.src().map(|s| s.path_string()),
129 err.error()
130 );
131 eprintln!("Debugging information: {:?}", err.debug());
132 unreachable!();
133 }
134 MessageView::Eos(..) => break,
135 _ => (),
136 }
137 }
138
139 pipeline
140 .set_state(gst::State::Null)
141 .expect("Unable to set the pipeline to the `Playing` state");
142
143 let expected_output = include_bytes!("sample.mp3");
144
145 let mut adapter = adapter.lock().unwrap();
146 let available = adapter.available();
147 assert_eq!(available, expected_output.len());
148 let output_buffer = adapter.take_buffer(available).unwrap();
149 let output = output_buffer.map_readable().unwrap();
150 assert_eq!(expected_output.as_ref(), output.as_ref());
151 }
152
153 #[test]
test_pull_range()154 fn test_pull_range() {
155 init();
156
157 let pipeline = gst::Pipeline::new(Some("sodium-decrypter-pull-range-test"));
158 let input_path = {
159 let mut r = PathBuf::new();
160 r.push(env!("CARGO_MANIFEST_DIR"));
161 r.push("tests");
162 r.push("encrypted_sample");
163 r.set_extension("enc");
164 r
165 };
166
167 let filesrc = gst::ElementFactory::make("filesrc", None).unwrap();
168 filesrc
169 .set_property("location", &input_path.to_str().unwrap())
170 .expect("failed to set property");
171
172 let dec = gst::ElementFactory::make("sodiumdecrypter", None).unwrap();
173 dec.set_property("sender-key", &*SENDER_PUBLIC)
174 .expect("failed to set property");
175 dec.set_property("receiver-key", &*RECEIVER_PRIVATE)
176 .expect("failed to set property");
177
178 pipeline
179 .add_many(&[&filesrc, &dec])
180 .expect("failed to add elements to the pipeline");
181 gst::Element::link_many(&[&filesrc, &dec]).expect("failed to link the elements");
182
183 // Activate in the pad in pull mode
184 pipeline
185 .set_state(gst::State::Ready)
186 .expect("Unable to set the pipeline to the `Playing` state");
187 let srcpad = dec.static_pad("src").unwrap();
188 srcpad.activate_mode(gst::PadMode::Pull, true).unwrap();
189
190 pipeline
191 .set_state(gst::State::Playing)
192 .expect("Unable to set the pipeline to the `Playing` state");
193
194 // Test that the decryptor is seekable
195 let mut q = gst::query::Seeking::new(gst::Format::Bytes);
196 srcpad.query(&mut q);
197
198 // get the seeking capabilities
199 let (seekable, start, stop) = q.result();
200 assert_eq!(seekable, true);
201 assert_eq!(
202 start,
203 gst::GenericFormattedValue::Bytes(Some(gst::format::Bytes(0)))
204 );
205 assert_eq!(
206 stop,
207 gst::GenericFormattedValue::Bytes(Some(gst::format::Bytes(6043)))
208 );
209
210 // do pulls
211 let expected_array_1 = [
212 255, 251, 192, 196, 0, 0, 13, 160, 37, 86, 116, 240, 0, 42, 73, 33, 43, 63, 61, 16, 128, 5,
213 53, 37, 220, 28, 225, 35, 16, 243, 140, 220, 4, 192, 2, 64, 14, 3, 144, 203, 67, 208, 244,
214 61, 70, 175, 103, 127, 28, 0,
215 ];
216 let buf1 = srcpad.range(0, 50).unwrap();
217 assert_eq!(buf1.size(), 50);
218 let map1 = buf1.map_readable().unwrap();
219 assert_eq!(&map1[..], &expected_array_1[..]);
220
221 let expected_array_2 = [
222 255, 251, 192, 196, 0, 0, 13, 160, 37, 86, 116, 240, 0, 42, 73, 33, 43, 63, 61, 16, 128, 5,
223 53, 37, 220, 28, 225, 35, 16, 243, 140, 220, 4, 192, 2, 64, 14, 3, 144, 203, 67, 208, 244,
224 61, 70, 175, 103, 127, 28, 0, 0, 0, 0, 12, 60, 60, 60, 122, 0, 0, 0, 12, 195, 195, 195,
225 207, 192, 0, 0, 3, 113, 195, 199, 255, 255, 254, 97, 225, 225, 231, 160, 0, 0, 49, 24, 120,
226 120, 121, 232, 0, 0, 12, 252, 195, 195, 199, 128, 0, 0, 0,
227 ];
228 let buf2 = srcpad.range(0, 100).unwrap();
229 assert_eq!(buf2.size(), 100);
230 let map2 = buf2.map_readable().unwrap();
231 assert_eq!(&map2[..], &expected_array_2[..]);
232
233 // compare the first 50 bytes of the two slices
234 // they should match
235 assert_eq!(&map1[..], &map2[..map1.len()]);
236
237 // request in the middle of a block
238 let buf = srcpad.range(853, 100).unwrap();
239 // result size doesn't include the block macs,
240 assert_eq!(buf.size(), 100);
241
242 // read till eos, this also will pull multiple blocks
243 let buf = srcpad.range(853, 42000).unwrap();
244 // 6031 (size of file) - 883 (requersted offset) - headers size - (numbler of blcks * block mac)
245 assert_eq!(buf.size(), 5054);
246
247 // read 0 bytes from the start
248 let buf = srcpad.range(0, 0).unwrap();
249 assert_eq!(buf.size(), 0);
250
251 // read 0 bytes somewhere in the middle
252 let buf = srcpad.range(4242, 0).unwrap();
253 assert_eq!(buf.size(), 0);
254
255 // read 0 bytes to eos
256 let res = srcpad.range(6003, 0);
257 assert_eq!(res, Err(gst::FlowError::Eos));
258
259 // read 100 bytes at eos
260 let res = srcpad.range(6003, 100);
261 assert_eq!(res, Err(gst::FlowError::Eos));
262
263 // read 100 bytes way past eos
264 let res = srcpad.range(424_242, 100);
265 assert_eq!(res, Err(gst::FlowError::Eos));
266
267 // read 10 bytes at eos -1, should return a single byte
268 let buf = srcpad.range(5906, 10).unwrap();
269 assert_eq!(buf.size(), 1);
270
271 pipeline
272 .set_state(gst::State::Null)
273 .expect("Unable to set the pipeline to the `Playing` state");
274 }
275
276 #[test]
test_state_changes()277 fn test_state_changes() {
278 init();
279
280 // NullToReady without keys provided
281 {
282 let dec = gst::ElementFactory::make("sodiumdecrypter", None).unwrap();
283 assert!(dec.change_state(gst::StateChange::NullToReady).is_err());
284
285 // Set only receiver key
286 let dec = gst::ElementFactory::make("sodiumdecrypter", None).unwrap();
287 dec.set_property("receiver-key", &*RECEIVER_PRIVATE)
288 .expect("failed to set property");
289 assert!(dec.change_state(gst::StateChange::NullToReady).is_err());
290
291 // Set only sender key
292 let dec = gst::ElementFactory::make("sodiumdecrypter", None).unwrap();
293 dec.set_property("sender-key", &*SENDER_PUBLIC)
294 .expect("failed to set property");
295 assert!(dec.change_state(gst::StateChange::NullToReady).is_err());
296 }
297
298 // NullToReady, no nonce provided
299 {
300 let dec = gst::ElementFactory::make("sodiumdecrypter", None).unwrap();
301 dec.set_property("sender-key", &*SENDER_PUBLIC)
302 .expect("failed to set property");
303 dec.set_property("receiver-key", &*RECEIVER_PRIVATE)
304 .expect("failed to set property");
305 assert!(dec.change_state(gst::StateChange::NullToReady).is_ok());
306 }
307
308 // ReadyToNull
309 {
310 let dec = gst::ElementFactory::make("sodiumdecrypter", None).unwrap();
311 dec.set_property("sender-key", &*SENDER_PUBLIC)
312 .expect("failed to set property");
313 dec.set_property("receiver-key", &*RECEIVER_PRIVATE)
314 .expect("failed to set property");
315 assert!(dec.change_state(gst::StateChange::NullToReady).is_ok());
316 }
317 }
318