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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pending_videos
pending_thumbnails
sample_data
current_version
wifi_password
example_app_data
state
test_data
Expand Down
10 changes: 5 additions & 5 deletions HOW_TO.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ cd /path-to-secluso/server/
cargo run --release
```

The server binds to 127.0.0.1 by default.
If you must use HTTP and need the server reachable on the network, run:
The server binds to 127.0.0.1:8000 by default.
If you need it reachable on the network, pass an explicit bind address and port:

```
cargo run --release --network-type=http
cargo run --release -- --bind-address=0.0.0.0 --port=8000
```

However, the server program might crash.
Expand All @@ -123,7 +123,7 @@ Therefore, we suggest using a systemd service to ensure that the server program
You can find instructions to do this online, e.g., ([here](https://www.shubhamdipt.com/blog/how-to-create-a-systemd-service-in-linux/)).

Here is an example of what the service file could look like.
If you need HTTP, add --network-type=http to ExecStart.
Set the bind address and port directly in ExecStart.

```
[Unit]
Expand All @@ -132,7 +132,7 @@ Description=secluso_server
[Service]
User=your-username
WorkingDirectory=/absolute-path-to-secluso-source/server/
ExecStart=/absolute-path-to-cargo-executable/cargo run --release --network-type=https
ExecStart=/absolute-path-to-cargo-executable/cargo run --release -- --bind-address=127.0.0.1 --port=8000
Restart=always
RestartSec=1

Expand Down
9 changes: 8 additions & 1 deletion camera_hub/src/pairing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ pub fn get_input_camera_secret() -> Vec<u8> {
data.to_vec()
}

// Read the WiFi password contents from file to use for the hotspot
pub fn get_input_wifi_password() -> String {
let pathname = "./wifi_password";
let contents = fs::read_to_string(pathname).expect("Failed to read from \"wifi_password\" file. You can generate this in config tool");
return contents;
}

fn invite(
stream: &mut TcpStream,
mls_client: &mut MlsClient,
Expand Down Expand Up @@ -489,7 +496,7 @@ pub fn create_wifi_hotspot() {
// less fragile than shell parsing to use argv
let _ = Command::new("nmcli")
.args([
"device", "wifi", "hotspot", "ssid", "Secluso", "password", "12345678",
"device", "wifi", "hotspot", "ssid", "Secluso", "password", get_input_wifi_password().as_str(),
])
.stdout(Stdio::null())
.stderr(Stdio::null())
Expand Down
73 changes: 63 additions & 10 deletions client_lib/src/pairing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@
//!
//! SPDX-License-Identifier: GPL-3.0-or-later

use std::fs;
use openmls::prelude::KeyPackage;
use serde::{Deserialize, Serialize};
use qrcode::QrCode;
use image::Luma;
use std::fs::create_dir;
use std::io::Write;
use anyhow::Context;
use std::path::Path;
use anyhow::{anyhow, Context};

use openmls_rust_crypto::OpenMlsRustCrypto;
use openmls_traits::random::OpenMlsRand;
use openmls_traits::OpenMlsProvider;
use rand::distributions::Uniform;
use rand::{Rng, thread_rng};


pub const NUM_SECRET_BYTES: usize = 72;
pub const CAMERA_SECRET_VERSION: &str = "v1.1";
const WIFI_PASSWORD_LEN: usize = 10;

// We version the QR code, store secret bytes as well (base64-url-encoded) as the Wi-Fi passphrase for Raspberry Pi cameras.
// Versioned QR codes can be helpful to ensure compatibility.
Expand All @@ -29,6 +35,9 @@ pub struct CameraSecret {
// But this shouldn't be "s" to maintain separation from the user credentials qr code
#[serde(rename = "cs", alias = "secret")]
pub secret: String,

#[serde(rename = "wp", alias = "wiif_password")]
pub wifi_password: Option<String>,
}

#[derive(Serialize, Deserialize, PartialEq)]
Expand Down Expand Up @@ -63,6 +72,7 @@ pub fn generate_ip_camera_secret(camera_name: &str) -> anyhow::Result<Vec<u8>> {
let camera_secret = CameraSecret {
version: CAMERA_SECRET_VERSION.to_string(),
secret: base64_url::encode(&secret),
wifi_password: None,
};

let writeable_secret = serde_json::to_string(&camera_secret).context("Failed to serialize camera secret into JSON")?;
Expand All @@ -79,33 +89,76 @@ pub fn generate_ip_camera_secret(camera_name: &str) -> anyhow::Result<Vec<u8>> {
Ok(secret)
}

pub fn generate_raspberry_camera_secret(dir: String) -> anyhow::Result<()> {
fn generate_wifi_password(dir: &Path) -> anyhow::Result<String> {
// Generate the randomized WiFi password
let wifi_password = generate_random(WIFI_PASSWORD_LEN, false); //10 characters that are upper/low alphanumeric
fs::File::create(dir.join("wifi_password")).context("Could not create wifi_password file")?;

fs::write(dir.join("wifi_password"), wifi_password.clone()).with_context(|| format!("Could not create {}", dir.display()))?;

Ok(wifi_password)
}

pub fn generate_random(num_chars: usize, special_characters: bool) -> String {
// We exclude : because that character has a special use in the http(s) auth header.
// We exclude / because that character is used within the Linux file system
let charset: &[u8] = if special_characters {
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789\
!@#$%^&*()-_=+[]{}|;,.<>?"
} else {
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789"
};

let mut rng = thread_rng();
(0..num_chars)
.map(|_| {
let idx = rng.sample(Uniform::new(0, charset.len()));
charset[idx] as char
})
.collect()
}

pub fn generate_raspberry_camera_secret(dir: &Path, error_on_folder_exist: bool) -> anyhow::Result<()> {
// If it already exists and we don't want to try re-generating credentials..
if dir.exists() && error_on_folder_exist {
return Err(anyhow!("The directory exists!"));
}

// Create the directory if it doesn't exist
if !dir.exists() {
create_dir(dir)?;
}

let crypto = OpenMlsRustCrypto::default();
let secret = crypto
.crypto()
.random_vec(NUM_SECRET_BYTES).context("Failed to generate camera secret bytes")?;

let wifi_password = generate_wifi_password(dir)?;
let camera_secret = CameraSecret {
version: CAMERA_SECRET_VERSION.to_string(),
secret: base64_url::encode(&secret),
wifi_password: Some(wifi_password),
};

let writeable_secret = serde_json::to_string(&camera_secret).context("Failed to serialize camera secret into JSON")?;

// Create the directory if it doesn't exist
create_dir(dir.clone()).context("Failed to create directory (it may already exist)")?;
let qr_content = serde_json::to_string(&camera_secret).context("Failed to serialize camera secret into JSON")?;

// Save in a file to be given to the camera
// The camera secret does not need to be versioned. We're not worried about the formatting ever changing.
// Just put the secret by itself in this file.
let mut file =
std::fs::File::create(dir.clone() + "/camera_secret").context("Could not create file")?;
std::fs::File::create(dir.join("camera_secret")).context("Could not create file")?;
file.write_all(&secret).context("Failed to write camera secret data to file")?;

// Save as QR code to be shown to the app
let code = QrCode::new(writeable_secret.clone()).context("Failed to generate QR code from camera secret bytes")?;
// Save as QR code to be shown to the app (with secret + version + wifi password)
let code = QrCode::new(qr_content.clone()).context("Failed to generate QR code from camera secret bytes")?;
let image = code.render::<Luma<u8>>().build();
image
.save(dir.clone() + "/camera_secret_qrcode.png").context("Failed to save QR code image")?;
.save(dir.join("camera_secret_qrcode.png")).context("Failed to save QR code image")?;

Ok(())
}
Expand Down
35 changes: 23 additions & 12 deletions client_server_lib/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
//!
//! SPDX-License-Identifier: GPL-3.0-or-later

use anyhow::Context;
use rand::distributions::Uniform;
use rand::{thread_rng, Rng};
use std::io;
use anyhow::Context;

pub const NUM_USERNAME_CHARS: usize = 14;
pub const NUM_PASSWORD_CHARS: usize = 14;
Expand All @@ -14,7 +14,7 @@ pub const USER_CREDENTIALS_VERSION: &str = "uc-v1.0";

#[derive(serde::Serialize, serde::Deserialize)]
pub struct UserCredentials {
#[serde(rename = "v", alias = "version")]
#[serde(rename = "v", alias = "version")]
pub version: String,

#[serde(rename = "u", alias = "username")]
Expand All @@ -23,8 +23,8 @@ pub struct UserCredentials {
#[serde(rename = "p", alias = "password")]
pub password: String,

#[serde(rename="sa", alias="server_addr")]
pub server_addr: String
#[serde(rename = "sa", alias = "server_addr")]
pub server_addr: String,
}

pub fn parse_user_credentials(credentials: Vec<u8>) -> io::Result<(String, String)> {
Expand Down Expand Up @@ -63,13 +63,19 @@ pub fn parse_user_credentials_full(
))
}

fn generate_random(num_chars: usize) -> String {
pub fn generate_random(num_chars: usize, special_characters: bool) -> String {
// We exclude : because that character has a special use in the http(s) auth header.
// We exclude / because that character is used within the Linux file system
let charset: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
let charset: &[u8] = if special_characters {
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789\
!@#$%^&*()-_=+[]{}|;,.<>?";
!@#$%^&*()-_=+[]{}|;,.<>?"
} else {
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789"
};

let mut rng = thread_rng();
(0..num_chars)
Expand All @@ -80,16 +86,21 @@ fn generate_random(num_chars: usize) -> String {
.collect()
}


pub fn create_user_credentials(server_addr: String) -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
let username = generate_random(NUM_USERNAME_CHARS);
let password = generate_random(NUM_PASSWORD_CHARS);
let username = generate_random(NUM_USERNAME_CHARS, true);
let password = generate_random(NUM_PASSWORD_CHARS, true);

let credentials_string = format!("{}{}", username, password);
let credentials = credentials_string.into_bytes();

let user_credentials = UserCredentials {version: USER_CREDENTIALS_VERSION.to_string(), username, password, server_addr};
let credentials_full_string = serde_json::to_string(&user_credentials).context("Failed to serialize user credentials into JSON")?;
let user_credentials = UserCredentials {
version: USER_CREDENTIALS_VERSION.to_string(),
username,
password,
server_addr,
};
let credentials_full_string = serde_json::to_string(&user_credentials)
.context("Failed to serialize user credentials into JSON")?;
let credentials_full = credentials_full_string.into_bytes();

Ok((credentials, credentials_full))
Expand Down
2 changes: 1 addition & 1 deletion client_server_lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! SPDX-License-Identifier: GPL-3.0-or-later
pub mod auth;
pub mod fault;
pub mod fault;
Loading
Loading