m365/
register.rs

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  /**
38   * Create new registration request for device. It is important that device is a M365 scooter, and you did already connect to it
39   */
40  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  /**
58   * Starting registration process. In some cases there will be RegistrationError.
59   * For this error please disconnect and connect again to scooter and ask user to press power button. Remember to create new instance of
60   * RegistrationRequest and start process again. I know this sucks but this is how it works.
61   */
62  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  /**
72   * Get remote info, this is used for generating token and did that is sent to scooter
73   */
74  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  /**
85   * Send public key to scooter and then wait for scooter with
86   */
87  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        // something bad happened, error
162        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}