fxa_client/internal/
send_tab.rsuse super::{
commands::{
decrypt_command, encrypt_command, get_public_keys,
send_tab::{self, SendTabPayload},
IncomingDeviceCommand, PrivateCommandKeys as PrivateSendTabKeys,
PublicCommandKeys as PublicSendTabKeys,
},
http_client::GetDeviceResponse,
scopes, telemetry, FirefoxAccount,
};
use crate::{Error, Result};
impl FirefoxAccount {
pub(crate) fn load_or_generate_send_tab_keys(&mut self) -> Result<PrivateSendTabKeys> {
if let Some(s) = self.send_tab_key() {
match PrivateSendTabKeys::deserialize(s) {
Ok(keys) => return Ok(keys),
Err(_) => {
error_support::report_error!(
"fxaclient-send-tab-key-deserialize",
"Could not deserialize Send Tab keys. Re-creating them."
);
}
}
}
let keys = PrivateSendTabKeys::from_random()?;
self.set_send_tab_key(keys.serialize()?);
Ok(keys)
}
pub fn send_single_tab(
&mut self,
target_device_id: &str,
title: &str,
url: &str,
) -> Result<()> {
let devices = self.get_devices(false)?;
let target = devices
.iter()
.find(|d| d.id == target_device_id)
.ok_or_else(|| Error::UnknownTargetDevice(target_device_id.to_owned()))?;
let (payload, sent_telemetry) = SendTabPayload::single_tab(title, url);
let oldsync_key = self.get_scoped_key(scopes::OLD_SYNC)?;
let command_payload =
encrypt_command(oldsync_key, target, send_tab::COMMAND_NAME, &payload)?;
self.invoke_command(send_tab::COMMAND_NAME, target, &command_payload, None)?;
self.telemetry.record_command_sent(sent_telemetry);
Ok(())
}
pub(crate) fn handle_send_tab_command(
&mut self,
sender: Option<GetDeviceResponse>,
payload: serde_json::Value,
reason: telemetry::ReceivedReason,
) -> Result<IncomingDeviceCommand> {
let send_tab_key: PrivateSendTabKeys = match self.send_tab_key() {
Some(s) => PrivateSendTabKeys::deserialize(s)?,
None => {
return Err(Error::IllegalState(
"Cannot find send-tab keys. Has initialize_device been called before?",
));
}
};
match decrypt_command(payload, &send_tab_key) {
Ok(payload) => {
let recd_telemetry = telemetry::ReceivedCommand::for_send_tab(&payload, reason);
self.telemetry.record_command_received(recd_telemetry);
Ok(IncomingDeviceCommand::TabReceived { sender, payload })
}
Err(e) => {
log::warn!("Could not decrypt Send Tab payload. Diagnosing then resetting the Send Tab keys.");
match self.diagnose_remote_keys(send_tab_key) {
Ok(_) => {
error_support::report_error!(
"fxaclient-send-tab-decrypt",
"Could not find the cause of the Send Tab keys issue."
);
}
Err(e) => {
error_support::report_error!("fxaclient-send-tab-decrypt", "{}", e);
}
};
self.clear_send_tab_key();
self.reregister_current_capabilities()?;
Err(e)
}
}
}
fn send_tab_key(&self) -> Option<&str> {
self.state.get_commands_data(send_tab::COMMAND_NAME)
}
fn set_send_tab_key(&mut self, key: String) {
self.state.set_commands_data(send_tab::COMMAND_NAME, key)
}
fn clear_send_tab_key(&mut self) {
self.state.clear_commands_data(send_tab::COMMAND_NAME);
}
fn diagnose_remote_keys(&mut self, local_send_tab_key: PrivateSendTabKeys) -> Result<()> {
let own_device = &mut self
.get_current_device()?
.ok_or(Error::SendTabDiagnosisError("No remote device."))?;
let oldsync_key = self.get_scoped_key(scopes::OLD_SYNC)?;
let public_keys_remote = get_public_keys(oldsync_key, own_device, send_tab::COMMAND_NAME)
.map_err(|_| {
Error::SendTabDiagnosisError("Unable to decrypt public key bundle.")
})?;
let public_keys_local: PublicSendTabKeys = local_send_tab_key.into();
if public_keys_local.public_key() != public_keys_remote.public_key() {
return Err(Error::SendTabDiagnosisError("Mismatch in public key."));
}
if public_keys_local.auth_secret() != public_keys_remote.auth_secret() {
return Err(Error::SendTabDiagnosisError("Mismatch in auth secret."));
}
Ok(())
}
}