Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8use crate::device::{
9 hardware::{Hardware, HardwareCommand, HardwareEvent, HardwareSubscribeCmd, HardwareWriteCmd},
10 protocol::{
11 ProtocolHandler,
12 ProtocolIdentifier,
13 ProtocolInitializer,
14 generic_protocol_initializer_setup,
15 },
16};
17use aes::Aes128;
18use async_trait::async_trait;
19use buttplug_core::errors::ButtplugDeviceError;
20use buttplug_server_device_config::Endpoint;
21use buttplug_server_device_config::{
22 ProtocolCommunicationSpecifier,
23 ServerDeviceDefinition,
24 UserDeviceIdentifier,
25};
26use ecb::cipher::block_padding::Pkcs7;
27use ecb::cipher::{BlockDecryptMut, BlockEncryptMut, KeyInit};
28use std::sync::{
29 Arc,
30 atomic::{AtomicU8, Ordering},
31};
32use uuid::{Uuid, uuid};
33
34use rand::distributions::Alphanumeric;
35use rand::{Rng, thread_rng};
36use regex::Regex;
37use sha2::{Digest, Sha256};
38
39type Aes128EcbEnc = ecb::Encryptor<Aes128>;
40type Aes128EcbDec = ecb::Decryptor<Aes128>;
41
42const VIBCRAFTER_PROTOCOL_UUID: Uuid = uuid!("d3721a71-a81d-461a-b404-8599ce50c00b");
43const VIBCRAFTER_KEY: [u8; 16] = *b"jdk#Cra%f5Vib28r";
44
45generic_protocol_initializer_setup!(VibCrafter, "vibcrafter");
46
47#[derive(Default)]
48pub struct VibCrafterInitializer {}
49
50fn encrypt(command: String) -> Vec<u8> {
51 let enc = Aes128EcbEnc::new(&VIBCRAFTER_KEY.into());
52 let res = enc.encrypt_padded_vec_mut::<Pkcs7>(command.as_bytes());
53
54 info!("Encoded {} to {:?}", command, res);
55 res
56}
57
58fn decrypt(data: Vec<u8>) -> String {
59 let dec = Aes128EcbDec::new(&VIBCRAFTER_KEY.into());
60 let res = String::from_utf8(dec.decrypt_padded_vec_mut::<Pkcs7>(&data).unwrap()).unwrap();
61
62 info!("Decoded {} from {:?}", res, data);
63 res
64}
65
66#[async_trait]
67impl ProtocolInitializer for VibCrafterInitializer {
68 async fn initialize(
69 &mut self,
70 hardware: Arc<Hardware>,
71 _: &ServerDeviceDefinition,
72 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
73 let mut event_receiver = hardware.event_stream();
74 hardware
75 .subscribe(&HardwareSubscribeCmd::new(
76 VIBCRAFTER_PROTOCOL_UUID,
77 Endpoint::Rx,
78 ))
79 .await?;
80
81 let auth_str = thread_rng()
82 .sample_iter(&Alphanumeric)
83 .take(6)
84 .map(char::from)
85 .collect::<String>();
86 let auth_msg = format!("Auth:{};", auth_str);
87 hardware
88 .write_value(&HardwareWriteCmd::new(
89 &[VIBCRAFTER_PROTOCOL_UUID],
90 Endpoint::Tx,
91 encrypt(auth_msg),
92 false,
93 ))
94 .await?;
95
96 loop {
97 let event = event_receiver.recv().await;
98 if let Ok(HardwareEvent::Notification(_, _, n)) = event {
99 let decoded = decrypt(n);
100 if decoded.eq("OK;") {
101 debug!("VibCrafter authenticated!");
102 return Ok(Arc::new(VibCrafter::default()));
103 }
104 let challenge = Regex::new(r"^[a-zA-Z0-9]{4}:([a-zA-Z0-9]+);$")
105 .expect("This is static and should always compile");
106 if let Some(parts) = challenge.captures(decoded.as_str()) {
107 debug!("VibCrafter challenge {:?}", parts);
108 if let Some(to_hash) = parts.get(1) {
109 debug!("VibCrafter to hash {:?}", to_hash);
110 let mut sha256 = Sha256::new();
111 sha256.update(to_hash.as_str().as_bytes());
112 let result = &sha256.finalize();
113
114 let auth_msg = format!("Auth:{:02x}{:02x};", result[0], result[1]);
115 hardware
116 .write_value(&HardwareWriteCmd::new(
117 &[VIBCRAFTER_PROTOCOL_UUID],
118 Endpoint::Tx,
119 encrypt(auth_msg),
120 false,
121 ))
122 .await?;
123 } else {
124 return Err(ButtplugDeviceError::ProtocolSpecificError(
125 "VibCrafter".to_owned(),
126 "VibCrafter didn't provide a valid security handshake".to_owned(),
127 ));
128 }
129 } else {
130 return Err(ButtplugDeviceError::ProtocolSpecificError(
131 "VibCrafter".to_owned(),
132 "VibCrafter didn't provide a valid security handshake".to_owned(),
133 ));
134 }
135 } else {
136 return Err(ButtplugDeviceError::ProtocolSpecificError(
137 "VibCrafter".to_owned(),
138 "VibCrafter didn't provide a valid security handshake".to_owned(),
139 ));
140 }
141 }
142 }
143}
144
145#[derive(Default)]
146pub struct VibCrafter {
147 speeds: [AtomicU8; 2],
148}
149
150impl ProtocolHandler for VibCrafter {
151 fn handle_output_vibrate_cmd(
152 &self,
153 feature_index: u32,
154 feature_id: uuid::Uuid,
155 speed: u32,
156 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
157 self.speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed);
158
159 Ok(vec![
160 HardwareWriteCmd::new(
161 &[feature_id],
162 Endpoint::Tx,
163 encrypt(format!(
164 "MtInt:{:02}{:02};",
165 self.speeds[0].load(Ordering::Relaxed),
166 self.speeds[1].load(Ordering::Relaxed)
167 )),
168 false,
169 )
170 .into(),
171 ])
172 }
173}