1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
pub use crate::commands::send_tab::{SendTabPayload, TabHistoryEntry};
use crate::{
commands::send_tab::{
self, EncryptedSendTabPayload, PrivateSendTabKeys, PublicSendTabKeys, SendTabKeysPayload,
},
error::*,
http_client::GetDeviceResponse,
scopes, telemetry, FirefoxAccount, IncomingDeviceCommand,
};
impl FirefoxAccount {
pub(crate) fn generate_send_tab_command_data(&mut self) -> Result<String> {
let own_keys = self.load_or_generate_keys()?;
let public_keys: PublicSendTabKeys = own_keys.into();
let oldsync_key = self.get_scoped_key(scopes::OLD_SYNC)?;
public_keys.as_command_data(&oldsync_key)
}
fn load_or_generate_keys(&mut self) -> Result<PrivateSendTabKeys> {
if let Some(s) = self.state.commands_data.get(send_tab::COMMAND_NAME) {
match PrivateSendTabKeys::deserialize(s) {
Ok(keys) => return Ok(keys),
Err(_) => log::error!("Could not deserialize Send Tab keys. Re-creating them."),
}
}
let keys = PrivateSendTabKeys::from_random()?;
self.state
.commands_data
.insert(send_tab::COMMAND_NAME.to_owned(), keys.serialize()?);
Ok(keys)
}
pub fn send_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(|| ErrorKind::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 = send_tab::build_send_command(&oldsync_key, target, &payload)?;
self.invoke_command(send_tab::COMMAND_NAME, target, &command_payload)?;
self.telemetry.borrow_mut().record_tab_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.state.commands_data.get(send_tab::COMMAND_NAME) {
Some(s) => PrivateSendTabKeys::deserialize(s)?,
None => {
return Err(ErrorKind::IllegalState(
"Cannot find send-tab keys. Has initialize_device been called before?",
)
.into());
}
};
let encrypted_payload: EncryptedSendTabPayload = serde_json::from_value(payload)?;
match encrypted_payload.decrypt(&send_tab_key) {
Ok(payload) => {
let recd_telemetry = telemetry::ReceivedCommand {
flow_id: payload.flow_id.clone(),
stream_id: payload.stream_id.clone(),
reason,
};
self.telemetry
.borrow_mut()
.record_tab_received(recd_telemetry);
Ok(IncomingDeviceCommand::TabReceived { sender, payload })
}
Err(e) => {
log::error!("Could not decrypt Send Tab payload. Diagnosing then resetting the Send Tab keys.");
match self.diagnose_remote_keys(send_tab_key) {
Ok(_) => log::error!("Could not find the cause of the Send Tab keys issue."),
Err(e) => log::error!("{}", e),
};
self.state.commands_data.remove(send_tab::COMMAND_NAME);
self.reregister_current_capabilities()?;
Err(e)
}
}
}
fn diagnose_remote_keys(&mut self, local_send_tab_key: PrivateSendTabKeys) -> Result<()> {
let own_device = &mut self
.get_current_device()?
.ok_or_else(|| ErrorKind::SendTabDiagnosisError("No remote device."))?;
let command = own_device
.available_commands
.get(send_tab::COMMAND_NAME)
.ok_or_else(|| ErrorKind::SendTabDiagnosisError("No remote command."))?;
let bundle: SendTabKeysPayload = serde_json::from_str(command)?;
let oldsync_key = self.get_scoped_key(scopes::OLD_SYNC)?;
let public_keys_remote = bundle.decrypt(oldsync_key).map_err(|_| {
ErrorKind::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(ErrorKind::SendTabDiagnosisError("Mismatch in public key.").into());
}
if public_keys_local.auth_secret() != public_keys_remote.auth_secret() {
return Err(ErrorKind::SendTabDiagnosisError("Mismatch in auth secret.").into());
}
Ok(())
}
}