✨ mqtt connections
almost working, need encryption
This commit is contained in:
parent
20ab0153d1
commit
125d50530d
15 changed files with 1259 additions and 42 deletions
177
src/api.rs
177
src/api.rs
|
@ -1,8 +1,185 @@
|
|||
use age::{Recipient, secrecy::ExposeSecret};
|
||||
use based::auth::{Sessions, User};
|
||||
use minisign::PublicKey;
|
||||
use owl::{Deserialize, Serialize};
|
||||
use owl::{prelude::*, query, save, set_global_db};
|
||||
use rumqttc::{AsyncClient, Event, EventLoop, MqttOptions, Packet, Transport};
|
||||
use std::io::{Read, Seek};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
io::{BufReader, Cursor},
|
||||
net::SocketAddr,
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct JoinParams {
|
||||
pub join_token: Option<String>,
|
||||
pub machine_id: String,
|
||||
pub hostname: String,
|
||||
pub identity: (String, String),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct JoinResponse {
|
||||
pub token: String,
|
||||
pub identity: (String, String),
|
||||
pub mqtt: String,
|
||||
}
|
||||
|
||||
pub fn mqtt_connect(machine_id: &str, mqtt: &str) -> (rumqttc::AsyncClient, rumqttc::EventLoop) {
|
||||
let mqttoptions = if mqtt.starts_with("ws://") {
|
||||
log::warn!("Using unencrypted WebSocket connection");
|
||||
let mut mqttoptions = MqttOptions::new(
|
||||
machine_id,
|
||||
&format!("ws://{}", mqtt.trim_start_matches("ws://")),
|
||||
8000,
|
||||
);
|
||||
|
||||
mqttoptions.set_transport(Transport::Ws);
|
||||
mqttoptions.set_keep_alive(Duration::from_secs(60));
|
||||
mqttoptions
|
||||
} else {
|
||||
log::info!("Using encrypted WebSocket connection");
|
||||
let mut mqttoptions = MqttOptions::new(
|
||||
machine_id,
|
||||
&format!("wss://{}", mqtt.trim_start_matches("wss://")),
|
||||
8000,
|
||||
);
|
||||
|
||||
mqttoptions.set_transport(Transport::wss_with_default_config());
|
||||
mqttoptions.set_keep_alive(Duration::from_secs(60));
|
||||
mqttoptions
|
||||
};
|
||||
|
||||
AsyncClient::new(mqttoptions, 10)
|
||||
}
|
||||
|
||||
pub async fn run_event_loop(mut eventloop: EventLoop) {
|
||||
log::info!("Handling MQTT events");
|
||||
loop {
|
||||
match eventloop.poll().await {
|
||||
Ok(Event::Incoming(incoming)) => {
|
||||
log::trace!("Incoming = {:?}", incoming);
|
||||
match incoming {
|
||||
Packet::Publish(publish) => {
|
||||
let s = publish.payload;
|
||||
// TODO : client decryption here
|
||||
println!("got payload {}", String::from_utf8(s.to_vec()).unwrap());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(Event::Outgoing(outgoing)) => {
|
||||
log::trace!("Outgoing = {:?}", outgoing);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("MQTT eventloop error = {:?}", e);
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Identity {
|
||||
pub age: age::x25519::Identity,
|
||||
pub sign: minisign::KeyPair,
|
||||
}
|
||||
|
||||
fn save_parts(part1: &[u8], part2: &[u8]) -> Vec<u8> {
|
||||
let mut vec = Vec::new();
|
||||
|
||||
let offset = part1.len() as u32; // assume it fits into u32
|
||||
vec.extend_from_slice(&offset.to_le_bytes()); // or to_be_bytes() for big-endian
|
||||
|
||||
vec.extend_from_slice(part1);
|
||||
vec.extend_from_slice(part2);
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
fn load_parts(data: &[u8]) -> (Vec<u8>, Vec<u8>) {
|
||||
let offset_bytes: [u8; 4] = data[..4].try_into().unwrap();
|
||||
let offset = u32::from_le_bytes(offset_bytes) as usize;
|
||||
|
||||
let part1 = data[4..4 + offset].to_vec();
|
||||
let part2 = data[4 + offset..].to_vec();
|
||||
|
||||
(part1, part2)
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
pub fn new() -> Self {
|
||||
let age = age::x25519::Identity::generate();
|
||||
let sign = minisign::KeyPair::generate_encrypted_keypair(Some(String::new())).unwrap();
|
||||
Self { age, sign }
|
||||
}
|
||||
|
||||
pub fn public(&self) -> (String, String) {
|
||||
(self.pub_key_age(), self.pub_key_sign())
|
||||
}
|
||||
|
||||
pub fn pub_key_age(&self) -> String {
|
||||
self.age.to_public().to_string()
|
||||
}
|
||||
|
||||
pub fn pub_key_sign(&self) -> String {
|
||||
self.sign.pk.to_box().unwrap().to_string()
|
||||
}
|
||||
|
||||
pub fn save(&self, dir: &PathBuf) {
|
||||
let age_key = self.age.to_string();
|
||||
std::fs::write(dir.join("age.key"), age_key.expose_secret().to_string()).unwrap();
|
||||
|
||||
let kbx = self.sign.sk.to_box(None).unwrap();
|
||||
|
||||
std::fs::write(dir.join("sign.key"), kbx.into_string()).unwrap();
|
||||
log::info!("Saved identity to {dir:?}");
|
||||
}
|
||||
|
||||
pub fn try_load(dir: &PathBuf) -> Option<Self> {
|
||||
let age =
|
||||
age::x25519::Identity::from_str(&std::fs::read_to_string(dir.join("age.key")).ok()?)
|
||||
.unwrap();
|
||||
let kbx = minisign::SecretKeyBox::from_string(
|
||||
&std::fs::read_to_string(dir.join("sign.key")).ok()?,
|
||||
)
|
||||
.unwrap();
|
||||
let sign = kbx.into_secret_key(Some(String::new())).unwrap();
|
||||
let pk = minisign::PublicKey::from_secret_key(&sign).ok()?;
|
||||
let kp = minisign::KeyPair { pk: pk, sk: sign };
|
||||
log::info!("Loaded identity from {dir:?}");
|
||||
Some(Self { age, sign: kp })
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, data: &[u8], recipient: &impl Recipient) -> Vec<u8> {
|
||||
let signed =
|
||||
minisign::sign(None, &self.sign.sk, data, Some("mynameisanubis"), None).unwrap();
|
||||
let signed = save_parts(&signed.to_bytes(), data);
|
||||
let enc = age::encrypt(recipient, &signed).unwrap();
|
||||
enc
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, data: &[u8], pk: &PublicKey) -> Option<Vec<u8>> {
|
||||
let dec = age::decrypt(&self.age, data).unwrap();
|
||||
let (sig, data) = load_parts(&dec);
|
||||
let sign_box =
|
||||
minisign::SignatureBox::from_string(&String::from_utf8(sig).unwrap()).unwrap();
|
||||
if minisign::verify(
|
||||
pk,
|
||||
&sign_box,
|
||||
BufReader::new(Cursor::new(data.clone())),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
return Some(data);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue