Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/rustpatcher-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn, Expr};
use syn::{Expr, ItemFn, parse_macro_input};

#[proc_macro_attribute]
pub fn public_key(args: TokenStream, input: TokenStream) -> TokenStream {
Expand All @@ -27,4 +27,4 @@ pub fn public_key(args: TokenStream, input: TokenStream) -> TokenStream {
};

TokenStream::from(expanded)
}
}
14 changes: 7 additions & 7 deletions crates/rustpatcher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rustpatcher"
version = "0.2.1"
version = "0.2.2"
edition = "2024"
description = "distributed patching system for single binary applications"
license = "MIT"
Expand All @@ -14,15 +14,15 @@ categories = ["network-programming"]
[dependencies]
rustpatcher_macros = { path = "../rustpatcher-macros", package = "rustpatcher-macros", version = "0.2.0" }

ctor = "0.5"
actor-helper = "0.1"
ctor = "0.6"
actor-helper = { version = "0.2", features = ["tokio","anyhow"] }
tokio ={ version = "1", features = ["rt-multi-thread","macros","sync"] }
anyhow = "1"
serde = { version = "1", default-features = false, features = ["derive"] }
serde_json = "1"
ed25519-dalek = { version = "2", features = ["serde", "rand_core"] }
ed25519-dalek = { version = "3.0.0-pre.1", features = ["serde", "rand_core"] }
sha2 = "0.10"
rand = "0.8"
rand = "0.9"
z32 = "1"
clap = { version = "4", features = ["derive"] }
once_cell = "1"
Expand All @@ -34,10 +34,10 @@ chrono = { version = "0.4", default-features = false, features = ["std"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", default-features=false, features = ["std"] }

iroh = { version = "0.92", default-features = false }
iroh = { version = "0.94", default-features = false }
postcard = { version = "1" }

distributed-topic-tracker = { version="0.2.1", default-features = false }
distributed-topic-tracker = { version="0.2.4", default-features = false }

[[example]]
name = "platforms"
Expand Down
1 change: 0 additions & 1 deletion crates/rustpatcher/e2e_test/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const PUBLIC_KEY: &str = "bhafqhm8k9e7fzab7i7h6gie6oedncwyffautkngqsa9d1ohzuho";
#[tokio::main]
#[rustpatcher::public_key(PUBLIC_KEY)]
async fn main() -> anyhow::Result<()> {

rustpatcher::spawn(rustpatcher::UpdaterMode::Now).await?;
println!("{:?}", rustpatcher::Version::current()?);

Expand Down
1 change: 0 additions & 1 deletion crates/rustpatcher/examples/platforms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ async fn main() -> anyhow::Result<()> {
.with_thread_ids(true)
.init();


#[cfg(not(debug_assertions))]
{
rustpatcher::spawn(rustpatcher::UpdaterMode::At(13, 40)).await?;
Expand Down
22 changes: 13 additions & 9 deletions crates/rustpatcher/src/distributor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use actor_helper::{Action, Actor, Handle, act_ok};
use actor_helper::{Action, Actor, Handle, Receiver, act_ok};
use distributed_topic_tracker::unix_minute;
use iroh::{
Endpoint, NodeId,
Endpoint, EndpointId,
endpoint::VarInt,
protocol::{AcceptError, ProtocolHandler},
};
Expand All @@ -13,12 +13,12 @@ use crate::{Patch, PatchInfo};

#[derive(Debug, Clone)]
pub struct Distributor {
api: Handle<DistributorActor>,
api: Handle<DistributorActor, anyhow::Error>,
}

#[derive(Debug)]
struct DistributorActor {
rx: tokio::sync::mpsc::Receiver<Action<DistributorActor>>,
rx: Receiver<Action<DistributorActor>>,

self_patch_bytes: Vec<u8>,
endpoint: Endpoint,
Expand All @@ -27,7 +27,7 @@ struct DistributorActor {
impl Distributor {
pub fn new(endpoint: Endpoint) -> anyhow::Result<Self> {
let self_patch_bytes = postcard::to_allocvec(&Patch::from_self()?)?;
let (api, rx) = Handle::channel(32);
let (api, rx) = Handle::channel();
tokio::spawn(async move {
let mut actor = DistributorActor {
rx,
Expand All @@ -50,15 +50,19 @@ impl Distributor {
.into_bytes()
}

pub async fn get_patch(&self, node_id: NodeId, patch_info: PatchInfo) -> anyhow::Result<Patch> {
pub async fn get_patch(
&self,
endpoint_id: EndpointId,
patch_info: PatchInfo,
) -> anyhow::Result<Patch> {
let endpoint = self
.api
.call(act_ok!(actor => async move {
actor.endpoint.clone()
}))
.await?;

let conn = endpoint.connect(node_id, &Distributor::ALPN()).await?;
let conn = endpoint.connect(endpoint_id, &Distributor::ALPN()).await?;
let (mut tx, mut rx) = conn.open_bi().await?;

// auth: hash(owner_pub_key + unix_minute)
Expand Down Expand Up @@ -88,11 +92,11 @@ impl Distributor {
}
}

impl Actor for DistributorActor {
impl Actor<anyhow::Error> for DistributorActor {
async fn run(&mut self) -> anyhow::Result<()> {
loop {
tokio::select! {
Some(action) = self.rx.recv() => {
Ok(action) = self.rx.recv_async() => {
action(self).await
}
}
Expand Down
5 changes: 4 additions & 1 deletion crates/rustpatcher/src/embed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ const _: () = {
#[cfg_attr(target_os = "macos", unsafe(link_section = "__DATA,__embsig"))]
#[cfg_attr(target_os = "linux", unsafe(link_section = ".embsig"))]
#[cfg_attr(target_os = "windows", unsafe(link_section = ".embsig"))]
#[cfg_attr(not(any(target_os = "macos", target_os = "linux", target_os = "windows")), unsafe(link_section = ".embsig"))]
#[cfg_attr(
not(any(target_os = "macos", target_os = "linux", target_os = "windows")),
unsafe(link_section = ".embsig")
)]
#[used]
#[unsafe(no_mangle)]
pub static EMBED_REGION: [u8; EMBED_REGION_LEN] = {
Expand Down
7 changes: 4 additions & 3 deletions crates/rustpatcher/src/macho.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use goblin::mach::{Mach, MachO};

pub fn exclude_code_signature(data: &[u8]) -> Result<Vec<u8>> {
Expand All @@ -18,7 +18,8 @@ fn exclude_from_macho(data: &[u8], macho: &MachO) -> Result<Vec<u8>> {
if offset > data.len() {
return Err(anyhow!(
"Code signature offset out of bounds: offset={}, file_len={}",
offset, data.len()
offset,
data.len()
));
}

Expand All @@ -31,4 +32,4 @@ fn exclude_from_macho(data: &[u8], macho: &MachO) -> Result<Vec<u8>> {

// No code signature found, return original
Ok(data.to_vec())
}
}
14 changes: 4 additions & 10 deletions crates/rustpatcher/src/patch.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::Version;
use ed25519_dalek::{Signature, SigningKey, ed25519::signature::SignerMut};
use ed25519_dalek::{Signature, Signer, SigningKey};
use serde::{Deserialize, Serialize};
use sha2::Digest;

Expand Down Expand Up @@ -59,23 +59,17 @@ impl Patch {
Ok(())
}

pub fn sign(
owner_signing_key: SigningKey,
data: &[u8],
) -> anyhow::Result<PatchInfo> {

pub fn sign(owner_signing_key: SigningKey, data: &[u8]) -> anyhow::Result<PatchInfo> {
#[cfg(target_os = "macos")]
let data_stripped = crate::macho::exclude_code_signature(data)?;
#[cfg(target_os = "macos")]
let data_stripped = data_stripped.as_slice();
#[cfg(not(target_os = "macos"))]
let data_stripped = data;

let (data_no_embed, data_embed, _) =
crate::embed::cut_embed_section(data_stripped)?;
let (data_no_embed, data_embed, _) = crate::embed::cut_embed_section(data_stripped)?;
let version = crate::embed::get_embedded_version(&data_embed)?;

let mut owner_siging_key = owner_signing_key;
let mut data_hasher = sha2::Sha512::new();
data_hasher.update(data_no_embed.as_slice());
let data_hash = data_hasher.finalize()[..32].try_into()?;
Expand All @@ -85,7 +79,7 @@ impl Patch {
sign_hash.update(data_hash);
sign_hash.update((data_no_embed.len() as u64).to_le_bytes());
let sign_hash = sign_hash.finalize();
let signature = owner_siging_key.sign(&sign_hash);
let signature = owner_signing_key.sign(&sign_hash);

Ok(PatchInfo {
version,
Expand Down
23 changes: 12 additions & 11 deletions crates/rustpatcher/src/patcher.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::{str::FromStr, sync::Mutex};

use actor_helper::{Action, Actor, Handle};
use actor_helper::{Action, Actor, Handle, Receiver};
use distributed_topic_tracker::{RecordPublisher, RecordTopic};
use ed25519_dalek::SigningKey;
use iroh::{Endpoint, protocol::Router};
use once_cell::sync::OnceCell;
use sha2::Digest;
Expand Down Expand Up @@ -49,7 +50,9 @@ impl Builder {

#[cfg_attr(debug_assertions, allow(dead_code))]
pub async fn build(self) -> anyhow::Result<Patcher> {
let secret_key = iroh::SecretKey::generate(rand::rngs::OsRng);
let secret_key = iroh::SecretKey::generate(&mut rand::rng());
let signing_key = SigningKey::from_bytes(&secret_key.to_bytes());

let topic_id = RecordTopic::from_str(
format!(
"rustpatcher:{}",
Expand All @@ -64,8 +67,8 @@ impl Builder {

let record_publisher = RecordPublisher::new(
topic_id,
secret_key.public().public(),
secret_key.secret().clone(),
signing_key.verifying_key(),
signing_key,
None,
initial_secret,
);
Expand All @@ -75,8 +78,6 @@ impl Builder {

let endpoint = Endpoint::builder()
.secret_key(secret_key.clone())
//.add_discovery(DnsDiscovery::n0_dns())
.discovery_n0()
.bind()
.await?;

Expand All @@ -99,12 +100,12 @@ impl Builder {

#[derive(Debug, Clone)]
pub struct Patcher {
_api: Handle<PatcherActor>,
_api: Handle<PatcherActor, anyhow::Error>,
}

#[derive(Debug)]
struct PatcherActor {
rx: tokio::sync::mpsc::Receiver<Action<PatcherActor>>,
rx: Receiver<Action<PatcherActor>>,

publisher: Publisher,
updater: Option<Updater>,
Expand All @@ -131,7 +132,7 @@ impl Patcher {
endpoint: Endpoint,
router: Router,
) -> Self {
let (api, rx) = Handle::channel(32);
let (api, rx) = Handle::channel();
tokio::spawn(async move {
let mut actor = PatcherActor {
rx,
Expand All @@ -152,11 +153,11 @@ impl Patcher {
}
}

impl Actor for PatcherActor {
impl Actor<anyhow::Error> for PatcherActor {
async fn run(&mut self) -> anyhow::Result<()> {
loop {
tokio::select! {
Some(action) = self.rx.recv() => {
Ok(action) = self.rx.recv_async() => {
action(self).await
}
Some(_) = self.update_receiver.recv(), if self.updater.is_none() => {
Expand Down
22 changes: 11 additions & 11 deletions crates/rustpatcher/src/publisher.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use actor_helper::{Action, Actor, Handle, act_ok};
use actor_helper::{Action, Actor, Handle, Receiver, act_ok};
use distributed_topic_tracker::{RecordPublisher, unix_minute};
use iroh::NodeId;
use iroh::EndpointId;
use tracing::{debug, error, warn};

use crate::{Patch, PatchInfo, Version};

#[derive(Debug, Clone)]
pub struct Publisher {
api: Handle<PublisherActor>,
api: Handle<PublisherActor, anyhow::Error>,
}

#[derive(Debug, Clone)]
Expand All @@ -18,7 +18,7 @@ pub enum PublisherState {

#[derive(Debug)]
struct PublisherActor {
rx: tokio::sync::mpsc::Receiver<Action<PublisherActor>>,
rx: Receiver<Action<PublisherActor>>,
state: PublisherState,

interval: tokio::time::Interval,
Expand All @@ -34,7 +34,7 @@ impl Publisher {
update_starter: tokio::sync::mpsc::Sender<()>,
) -> anyhow::Result<Self> {
let self_patch = Patch::from_self()?;
let (api, rx) = Handle::channel(32);
let (api, rx) = Handle::channel();
tokio::spawn(async move {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(55));
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
Expand Down Expand Up @@ -63,11 +63,11 @@ impl Publisher {
}
}

impl Actor for PublisherActor {
impl Actor<anyhow::Error> for PublisherActor {
async fn run(&mut self) -> anyhow::Result<()> {
loop {
tokio::select! {
Some(action) = self.rx.recv() => {
Ok(action) = self.rx.recv_async() => {
action(self).await
}
_ = self.interval.tick(), if matches!(self.state, PublisherState::Publishing) => {
Expand All @@ -80,17 +80,17 @@ impl Actor for PublisherActor {
let newer_patch_infos = records
.iter()
.filter_map(|r| if let Ok(patch_info) = r.content::<PatchInfo>(){
if let Ok(node_id) = NodeId::from_bytes(&r.node_id()) {
warn!("Found patch info: {:?}{:?}", node_id,patch_info);
Some((node_id,patch_info.clone()))
if let Ok(endpoint_id) = EndpointId::from_bytes(&r.node_id()) {
warn!("Found patch info: {:?}{:?}", endpoint_id,patch_info);
Some((endpoint_id,patch_info.clone()))
} else {
None
}
} else {
None
})
.filter(|(_,p)| p.version > c_version)
.collect::<Vec<(NodeId, PatchInfo)>>();
.collect::<Vec<(EndpointId, PatchInfo)>>();

warn!("Checked for updates, found {} newer versions", newer_patch_infos.len());
if newer_patch_infos.is_empty() {
Expand Down
Loading