diff --git a/crates/buttplug_server/src/device/protocol_impl/mod.rs b/crates/buttplug_server/src/device/protocol_impl/mod.rs index 2999de80..34dc8535 100644 --- a/crates/buttplug_server/src/device/protocol_impl/mod.rs +++ b/crates/buttplug_server/src/device/protocol_impl/mod.rs @@ -112,6 +112,7 @@ pub mod tryfun; pub mod tryfun_blackhole; pub mod tryfun_meta2; pub mod vibcrafter; +pub mod vibio; pub mod vibratissimo; pub mod vorze_sa; pub mod wetoy; @@ -540,6 +541,10 @@ pub fn get_default_protocol_map() -> HashMap; +type Aes128EcbDec = ecb::Decryptor; + +const VIBIO_PROTOCOL_UUID: Uuid = uuid!("b8c76c9e-cb42-4a94-99f4-7c2a8e5d3b2a"); +const VIBIO_KEY: [u8; 16] = *b"jdk#vib%y5fir21a"; + +generic_protocol_initializer_setup!(Vibio, "vibio"); + +#[derive(Default)] +pub struct VibioInitializer {} + +fn encrypt(command: String) -> Vec { + let enc = Aes128EcbEnc::new(&VIBIO_KEY.into()); + let res = enc.encrypt_padded_vec_mut::(command.as_bytes()); + + info!("Encoded {} to {:?}", command, res); + res +} + +fn decrypt(data: Vec) -> String { + let dec = Aes128EcbDec::new(&VIBIO_KEY.into()); + let res = String::from_utf8(dec.decrypt_padded_vec_mut::(&data).unwrap()).unwrap(); + + info!("Decoded {} from {:?}", res, data); + res +} + +#[async_trait] +impl ProtocolInitializer for VibioInitializer { + async fn initialize( + &mut self, + hardware: Arc, + _: &ServerDeviceDefinition, + ) -> Result, ButtplugDeviceError> { + let mut event_receiver = hardware.event_stream(); + hardware + .subscribe(&HardwareSubscribeCmd::new( + VIBIO_PROTOCOL_UUID, + Endpoint::Rx, + )) + .await?; + + let auth_str = rand::rng() + .sample_iter(&Alphanumeric) + .take(8) + .map(char::from) + .collect::(); + let auth_msg = format!("Auth:{};", auth_str); + hardware + .write_value(&HardwareWriteCmd::new( + &[VIBIO_PROTOCOL_UUID], + Endpoint::Tx, + encrypt(auth_msg), + false, + )) + .await?; + + loop { + let event = event_receiver.recv().await; + if let Ok(HardwareEvent::Notification(_, _, n)) = event { + let decoded = decrypt(n); + if decoded.eq("OK;") { + debug!("Vibio authenticated!"); + return Ok(Arc::new(Vibio::default())); + } + let challenge = Regex::new(r"^([0-9A-Fa-f]{4}):([^;]+);$") + .expect("This is static and should always compile"); + if let Some(parts) = challenge.captures(decoded.as_str()) { + debug!("Vibio challenge {:?}", parts); + if let Some(to_hash) = parts.get(2) { + debug!("Vibio to hash {:?}", to_hash); + let mut sha256 = Sha256::new(); + sha256.update(to_hash.as_str().as_bytes()); + let result = &sha256.finalize(); + + let auth_msg = format!("Auth:{:02x}{:02x};", result[0], result[1]); + hardware + .write_value(&HardwareWriteCmd::new( + &[VIBIO_PROTOCOL_UUID], + Endpoint::Tx, + encrypt(auth_msg), + false, + )) + .await?; + } else { + return Err(ButtplugDeviceError::ProtocolSpecificError( + "Vibio".to_owned(), + "Vibio didn't provide a valid security handshake".to_owned(), + )); + } + } else { + return Err(ButtplugDeviceError::ProtocolSpecificError( + "Vibio".to_owned(), + "Vibio didn't provide a valid security handshake".to_owned(), + )); + } + } else { + return Err(ButtplugDeviceError::ProtocolSpecificError( + "Vibio".to_owned(), + "Vibio didn't provide a valid security handshake".to_owned(), + )); + } + } + } +} + +#[derive(Default)] +pub struct Vibio { + speeds: [AtomicU8; 2], +} + +impl ProtocolHandler for Vibio { + fn handle_output_vibrate_cmd( + &self, + feature_index: u32, + feature_id: uuid::Uuid, + speed: u32, + ) -> Result, ButtplugDeviceError> { + self.speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed); + + Ok(vec![ + HardwareWriteCmd::new( + &[feature_id], + Endpoint::Tx, + encrypt(format!( + "MtInt:{:02}{:02};", + self.speeds[0].load(Ordering::Relaxed), + self.speeds[1].load(Ordering::Relaxed) + )), + false, + ) + .into(), + ]) + } +} diff --git a/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json b/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json index a853c012..310fe2de 100644 --- a/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json +++ b/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json @@ -1,7 +1,7 @@ { "version": { "major": 4, - "minor": 179 + "minor": 180 }, "protocols": { "activejoy": { @@ -21773,6 +21773,140 @@ "name": "VibCrafter Device" } }, + "vibio": { + "communication": [ + { + "btle": { + "names": [ + "Clara_Vibio", + "Dodson_Vibio", + "Elle_Vibio", + "Frida_Vibio", + "Rivera_Vibio" + ], + "services": { + "53300021-0050-4bd4-bbe5-a6920e4c5663": { + "rx": "53300023-0050-4bd4-bbe5-a6920e4c5663", + "tx": "53300022-0050-4bd4-bbe5-a6920e4c5663" + } + } + } + } + ], + "configurations": [ + { + "features": [ + { + "id": "343a8e18-b76c-4482-b048-32d762bf87c9", + "index": 0, + "output": { + "vibrate": { + "value": [ + 0, + 99 + ] + } + } + } + ], + "id": "b55fef0e-baa3-44d0-9545-a4b7b0298515", + "identifier": [ + "Clara_Vibio" + ], + "name": "Vibio Clara" + }, + { + "features": [ + { + "id": "343a8e18-b76c-4482-b048-32d762bf87c9", + "index": 0, + "output": { + "vibrate": { + "value": [ + 0, + 99 + ] + } + } + } + ], + "id": "c66fef0e-cbb4-44d0-9545-a4b7b0298516", + "identifier": [ + "Dodson_Vibio" + ], + "name": "Vibio Dodson" + }, + { + "id": "d77fef0e-dcc5-44d0-9545-a4b7b0298517", + "identifier": [ + "Rivera_Vibio" + ], + "name": "Vibio Rivera" + }, + { + "id": "e88fef0e-edd6-44d0-9545-a4b7b0298518", + "identifier": [ + "Elle_Vibio" + ], + "name": "Vibio Elle" + }, + { + "id": "f99fef0e-fee7-44d0-9545-a4b7b0298519", + "identifier": [ + "Frida_Vibio" + ], + "name": "Vibio Frida" + } + ], + "defaults": { + "features": [ + { + "id": "343a8e18-b76c-4482-b048-32d762bf87c9", + "index": 0, + "output": { + "vibrate": { + "value": [ + 0, + 99 + ] + } + } + }, + { + "id": "d92a031e-bd0d-4815-a0bd-6c59566dcce2", + "index": 1, + "output": { + "vibrate": { + "value": [ + 0, + 99 + ] + } + } + }, + { + "description": "Battery Level", + "id": "e1a2b3c4-d5e6-f7a0-b1c2-d3e4f5a6b7c8", + "index": 2, + "input": { + "battery": { + "command": [ + "Read" + ], + "value": [ + [ + 0, + 100 + ] + ] + } + } + } + ], + "id": "a44eef0e-b412-44d0-9545-a4b7b0298514", + "name": "Vibio Device" + } + }, "vibratissimo": { "communication": [ { diff --git a/crates/buttplug_server_device_config/device-config-v4/protocols/vibio.yml b/crates/buttplug_server_device_config/device-config-v4/protocols/vibio.yml new file mode 100644 index 00000000..4f4d992d --- /dev/null +++ b/crates/buttplug_server_device_config/device-config-v4/protocols/vibio.yml @@ -0,0 +1,68 @@ +--- +defaults: + name: Vibio Device + features: + - id: 343a8e18-b76c-4482-b048-32d762bf87c9 + output: + vibrate: + value: + - 0 + - 99 + index: 0 + - id: d92a031e-bd0d-4815-a0bd-6c59566dcce2 + output: + vibrate: + value: + - 0 + - 99 + index: 1 + id: a44eef0e-b412-44d0-9545-a4b7b0298514 +configurations: +- identifier: + - Clara_Vibio + name: Vibio Clara + features: + - id: 343a8e18-b76c-4482-b048-32d762bf87c9 + output: + vibrate: + value: + - 0 + - 99 + index: 0 + id: b55fef0e-baa3-44d0-9545-a4b7b0298515 +- identifier: + - Dodson_Vibio + name: Vibio Dodson + features: + - id: 343a8e18-b76c-4482-b048-32d762bf87c9 + output: + vibrate: + value: + - 0 + - 99 + index: 0 + id: c66fef0e-cbb4-44d0-9545-a4b7b0298516 +- identifier: + - Rivera_Vibio + name: Vibio Rivera + id: d77fef0e-dcc5-44d0-9545-a4b7b0298517 +- identifier: + - Elle_Vibio + name: Vibio Elle + id: e88fef0e-edd6-44d0-9545-a4b7b0298518 +- identifier: + - Frida_Vibio + name: Vibio Frida + id: f99fef0e-fee7-44d0-9545-a4b7b0298519 +communication: +- btle: + names: + - Clara_Vibio + - Dodson_Vibio + - Elle_Vibio + - Frida_Vibio + - Rivera_Vibio + services: + 53300021-0050-4bd4-bbe5-a6920e4c5663: + tx: 53300022-0050-4bd4-bbe5-a6920e4c5663 + rx: 53300023-0050-4bd4-bbe5-a6920e4c5663 diff --git a/crates/buttplug_server_device_config/device-config-v4/version.yaml b/crates/buttplug_server_device_config/device-config-v4/version.yaml index 87231a04..3f6c6f5b 100644 --- a/crates/buttplug_server_device_config/device-config-v4/version.yaml +++ b/crates/buttplug_server_device_config/device-config-v4/version.yaml @@ -1,3 +1,3 @@ version: major: 4 - minor: 179 + minor: 180