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