use crate::{BinaryError, ElectronApp, PatcherError};
use std::ops::Range;
#[cfg(test)]
use enum_iterator::IntoEnumIterator;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(test, derive(IntoEnumIterator))]
#[non_exhaustive]
pub enum Fuse {
RunAsNode,
EncryptedCookies,
NodeOptions,
NodeCliInspect,
EmbeddedAsarIntegrityValidation,
OnlyLoadAppFromAsar,
}
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub enum FuseStatus {
Present(bool),
Modified,
Removed,
}
impl Fuse {
const SENTINEL: &'static [u8] = b"dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX";
const DISABLED: u8 = b'0';
const ENABLED: u8 = b'1';
const REMOVED: u8 = b'r';
const EXPECTED_VERSION: u8 = 1;
fn schema_pos(&self) -> usize {
let wire_pos = match self {
Self::RunAsNode => 1,
Self::EncryptedCookies => 2,
Self::NodeOptions => 3,
Self::NodeCliInspect => 4,
Self::EmbeddedAsarIntegrityValidation => 5,
Self::OnlyLoadAppFromAsar => 6,
};
wire_pos - 1
}
pub(crate) fn find_wire(binary: &[u8]) -> Result<Range<usize>, PatcherError> {
let sentinel_len = Self::SENTINEL.len();
let pos = binary
.windows(sentinel_len)
.position(|slice| slice == Self::SENTINEL)
.ok_or(BinaryError::NoSentinel)?;
let start = pos + sentinel_len;
let version = binary.get(start).ok_or(BinaryError::NoFuseVersion)?;
if *version != Self::EXPECTED_VERSION {
return Err(PatcherError::FuseVersion {
expected: Self::EXPECTED_VERSION,
found: *version,
});
}
let len_pos = start + 1;
let wire_len = binary.get(len_pos).ok_or(BinaryError::NoFuseLength)?;
let wire_start = len_pos + 1;
let fuse_bytes = (wire_start)..(wire_start + usize::from(*wire_len));
Ok(fuse_bytes)
}
fn fuse_status(&self, wire: &[u8]) -> Result<FuseStatus, PatcherError> {
let status = wire
.get(self.schema_pos())
.ok_or(BinaryError::FuseDoesNotExist(*self))?;
let status = match *status {
Self::ENABLED => FuseStatus::Present(true),
Self::DISABLED => FuseStatus::Present(false),
Self::REMOVED => FuseStatus::Removed,
s => {
return Err(BinaryError::UnknownFuse {
fuse: *self,
value: s,
}
.into())
}
};
Ok(status)
}
fn disable(&self, wire: &mut [u8]) -> Result<FuseStatus, PatcherError> {
let mut enabled = self.fuse_status(wire)?;
match enabled {
FuseStatus::Present(e) if e => {
wire[self.schema_pos()] = Self::DISABLED;
enabled = FuseStatus::Modified
}
FuseStatus::Removed => return Err(PatcherError::RemovedFuse(*self)),
_ => {}
}
Ok(enabled)
}
fn enable(&self, wire: &mut [u8]) -> Result<FuseStatus, PatcherError> {
let mut enabled = self.fuse_status(wire)?;
match enabled {
FuseStatus::Present(e) if !e => {
wire[self.schema_pos()] = Self::ENABLED;
enabled = FuseStatus::Modified
}
FuseStatus::Removed => return Err(PatcherError::RemovedFuse(*self)),
_ => {}
}
Ok(enabled)
}
}
impl<'a> ElectronApp<'a> {
pub fn from_bytes(application_bytes: &'a mut [u8]) -> Result<ElectronApp<'a>, PatcherError> {
let wire_pos = Fuse::find_wire(application_bytes)?;
Ok(Self {
contents: application_bytes,
wire_start: wire_pos.start,
wire_end: wire_pos.end,
})
}
pub fn get_fuse_status(&self, fuse: Fuse) -> Result<FuseStatus, PatcherError> {
let wire = &self.contents[self.wire_start..self.wire_end];
fuse.fuse_status(wire)
}
pub fn set_fuse_status(
&mut self,
fuse: Fuse,
enabled: bool,
) -> Result<FuseStatus, PatcherError> {
let wire = &mut self.contents[self.wire_start..self.wire_end];
if enabled {
fuse.enable(wire)
} else {
fuse.disable(wire)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_BYTES: &[u8] = include_bytes!("../examples/fake_electron_fuses.bin");
const FUSE: Fuse = Fuse::RunAsNode;
fn get_wire() -> &'static [u8] {
let wire_pos = Fuse::find_wire(TEST_BYTES).unwrap();
&TEST_BYTES[wire_pos]
}
#[test]
fn sentinal_is_found() {
assert!(Fuse::find_wire(TEST_BYTES).is_ok());
}
#[test]
fn enabled_fuse_is_correct() {
assert_eq!(
FUSE.fuse_status(get_wire()).unwrap(),
FuseStatus::Present(true)
);
}
#[test]
fn disabled_fuse_is_correct() {
let mut wire = get_wire().to_vec();
assert_eq!(FUSE.disable(&mut wire).unwrap(), FuseStatus::Modified);
assert_eq!(FUSE.fuse_status(&wire).unwrap(), FuseStatus::Present(false));
}
#[test]
fn removed_fuse_is_correct() {
let mut wire = get_wire().to_vec();
wire[FUSE.schema_pos()] = Fuse::REMOVED;
assert_eq!(FUSE.fuse_status(&wire).unwrap(), FuseStatus::Removed);
}
#[test]
fn unknown_fuse_value_is_correct() {
let value = 9;
let mut wire = get_wire().to_vec();
wire[FUSE.schema_pos()] = value;
assert_eq!(
FUSE.fuse_status(&wire),
Err(PatcherError::Binary(BinaryError::UnknownFuse {
fuse: FUSE,
value,
}))
);
}
#[test]
fn modfying_removed_fuse_errors() {
let mut wire = get_wire().to_vec();
wire[FUSE.schema_pos()] = Fuse::REMOVED;
assert_eq!(
FUSE.disable(&mut wire),
Err(PatcherError::RemovedFuse(FUSE))
);
assert_eq!(FUSE.enable(&mut wire), Err(PatcherError::RemovedFuse(FUSE)));
}
#[test]
fn test_app_fuse_actions() {
let mut application_bytes = TEST_BYTES.to_vec();
let mut app = ElectronApp::from_bytes(&mut application_bytes).unwrap();
assert_eq!(
app.get_fuse_status(FUSE).unwrap(),
FuseStatus::Present(true)
);
assert_eq!(
app.set_fuse_status(FUSE, true).unwrap(),
FuseStatus::Present(true)
);
assert_eq!(
app.set_fuse_status(FUSE, false).unwrap(),
FuseStatus::Modified
);
assert_eq!(
app.get_fuse_status(FUSE).unwrap(),
FuseStatus::Present(false)
);
}
#[test]
fn can_read_all_fuses() {
let wire = get_wire();
for fuse in Fuse::into_enum_iter() {
assert!(matches!(
fuse.fuse_status(wire).unwrap(),
FuseStatus::Present(_)
));
}
}
#[test]
fn fuse_modifies_correct_position() {
let mut wire = get_wire().to_vec();
let fuse1 = Fuse::RunAsNode;
let fuse2 = Fuse::EncryptedCookies;
let fuse3 = Fuse::NodeOptions;
let fuse_2_original_status = fuse2.fuse_status(&wire).unwrap();
fuse1.disable(&mut wire).unwrap();
assert_eq!(fuse2.fuse_status(&wire).unwrap(), fuse_2_original_status);
let fuse_1_original_status = fuse1.fuse_status(&wire).unwrap();
fuse2.disable(&mut wire).unwrap();
assert_eq!(fuse1.fuse_status(&wire).unwrap(), fuse_1_original_status);
let left_fuse_original_status = fuse1.fuse_status(&wire).unwrap();
let right_fuse_original_status = fuse3.fuse_status(&wire).unwrap();
fuse2.enable(&mut wire).unwrap();
assert_eq!(fuse1.fuse_status(&wire).unwrap(), left_fuse_original_status);
assert_eq!(
fuse3.fuse_status(&wire).unwrap(),
right_fuse_original_status
);
}
}