1use crate::consts::{MiCommands, Registers};
2pub use crate::mi_crypto::AuthToken;
3use crate::protocol::MiProtocol;
4use crate::mi_crypto;
5
6use pretty_hex::*;
7use btleplug::platform::Peripheral;
8use p256::{PublicKey, ecdh::EphemeralSecret, EncodedPoint};
9use anyhow::{Result, anyhow};
10use thiserror::Error;
11
12#[derive(Error, Debug)]
13pub enum RegistrationError {
14 #[error("There was problem registering scooter")]
15 RegistrationFailed,
16 #[error("Please restart connection and try again")]
17 RestartNeeded,
18 #[error("Registration failed: {0}")]
19 Other(anyhow::Error)
20}
21
22impl From<anyhow::Error> for RegistrationError {
23 fn from(other: anyhow::Error) -> Self {
24 RegistrationError::Other(other)
25 }
26}
27
28pub struct RegistrationRequest {
29 protocol: MiProtocol,
30 my_secret_key: EphemeralSecret,
31 my_public_key: PublicKey,
32 remote_info: Option<Vec<u8>>,
33 token: Option<AuthToken>
34}
35
36impl RegistrationRequest {
37 pub async fn new(device : &Peripheral) -> Result<Self> {
41 let protocol = MiProtocol::new(device).await?;
42
43 let (my_secret_key, my_public_key) = mi_crypto::gen_key_pair();
44 tracing::debug!("Public key: {:?}", my_public_key);
45
46 let request = Self {
47 protocol,
48 my_secret_key,
49 my_public_key,
50 remote_info: None,
51 token: None
52 };
53
54 Ok(request)
55 }
56
57 pub async fn start(&mut self) -> Result<AuthToken, RegistrationError> {
63 self.read_remote_info().await?;
64 self.send_public_key().await?;
65 self.send_did().await?;
66 self.perform_auth().await?;
67
68 Ok(self.token.unwrap())
69 }
70
71 async fn read_remote_info(&mut self) -> Result<bool> {
75 self.protocol.write(&Registers::UPNP, MiCommands::CMD_GET_INFO).await?;
76
77 tracing::debug!("<- remote_info");
78 let remote_info = self.protocol.read_mi_parcel(&Registers::AVDTP).await?;
79 self.remote_info = Some(remote_info);
80
81 Ok(true)
82 }
83
84 async fn send_public_key(&mut self) -> Result<bool, RegistrationError> {
88 self.protocol.write(&Registers::UPNP, MiCommands::CMD_SET_KEY).await?;
89 self.protocol.write(&Registers::AVDTP, MiCommands::CMD_SEND_DATA).await?;
90
91 let notification = self.protocol.wait_for_notification().await;
92
93 if notification.is_err() {
94 return Err(RegistrationError::RestartNeeded)
95 }
96
97 let notification = notification.unwrap();
98
99 match MiCommands::try_from(notification) {
100 Ok(MiCommands::RCV_RDY) => {
101 tracing::debug!("<- {:?}", MiCommands::RCV_RDY);
102 let public_key_bytes = EncodedPoint::from(self.my_public_key);
103 tracing::debug!("-> Mi ready to receive key, uploading my public key: {:?}", public_key_bytes.as_bytes().hex_dump());
104 self.protocol.write_mi_parcel(&Registers::AVDTP, &public_key_bytes.as_bytes()[1..]).await?;
105 },
106 Ok(other) => {
107 tracing::debug!("Could not match: {:?}", other);
108 return Err(RegistrationError::Other(anyhow!("Scooter responded with: {:?} instead RCV_RDY", other)))
109 }
110 Err(err) => {
111 return Err(RegistrationError::Other(anyhow!(err)))
112 }
113 }
114
115 if let Some(MiCommands::RCV_OK) = self.protocol.next_mi_response().await {
116 tracing::debug!("Mi confirmed key receive");
117
118 return Ok(true)
119 }
120
121 Err(RegistrationError::Other(anyhow!("Sending public key failed...")))
122 }
123
124 async fn send_did(&mut self) -> Result<bool> {
125 let remote_key_bytes = self.protocol.read_mi_parcel(&Registers::AVDTP).await?;
126 let remote_info = self.remote_info.as_ref().unwrap();
127 let remote_key_bytes = [&[0x04], remote_key_bytes.as_slice()].concat();
128 let (did_ct, token) = mi_crypto::calc_did(&self.my_secret_key, &remote_key_bytes, &remote_info);
129
130 self.token = Some(token);
131 self.protocol.write(&Registers::AVDTP, MiCommands::CMD_SEND_DID).await?;
132
133 loop {
134 match self.protocol.next_mi_response().await {
135 Some(MiCommands::RCV_RDY) => {
136 tracing::debug!("Mi ready to receive, Sending did");
137 self.protocol.write_mi_parcel(&Registers::AVDTP, &did_ct).await?;
138 },
139 Some(MiCommands::RCV_OK) => {
140 tracing::debug!("Mi confirmed receiving did");
141 break;
142 }
143 _ => {
144 tracing::error!("Scooter did not receive public key");
145 return Err(anyhow!("Scooter did not receive public key"));
146 }
147 }
148 }
149
150 Ok(true)
151 }
152
153 async fn perform_auth(&mut self) -> Result<bool, RegistrationError> {
154 self.protocol.write(&Registers::UPNP, MiCommands::CMD_AUTH).await?;
155 match self.protocol.next_mi_response().await {
156 Some(MiCommands::RCV_AUTH_OK) => {
157 tracing::info!("Registered token: {:?}", self.token.unwrap().hex_dump());
158 },
159
160 Some(error) => {
161 tracing::error!("Registration failed: {:?}", error);
163 return Err(RegistrationError::RegistrationFailed)
164 },
165
166 None => {
167 tracing::error!("Registration failed, scooter did not respond");
168 return Err(RegistrationError::RegistrationFailed)
169 }
170 }
171
172 Ok(true)
173 }
174}