diff --git a/README.md b/README.md index 5c89d79..8f7f049 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,25 @@ ls /dev/cu.* espmonitor /dev/tty.usbserial-1420 ``` +### configure the hardware + +make a seed: `./sphinx-key/newseed.sh` + +make a `.env` file like: + +``` +SSID=my_ssid +PASS=my_wifi_password +BROKER=my_ip:1883 +SEED=my_seed +``` + +connect to the `sphinxkey` network on your computer + +`cargo run --bin config` + +This will encrypt your seed and send to the hardware, along with your home wifi information and broker address + # dependencies `cd sphinx-key` @@ -98,3 +117,11 @@ esptool.py --chip esp32c3 elf2image target/riscv32imc-esp-espidf/release/sphinx- esptool.py --chip esp32c3 -p /dev/tty.usbserial-1420 -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 4MB 0x10000 target/riscv32imc-esp-espidf/release/sphinx-key.bin espmonitor /dev/tty.usbserial-1420 + +### config + +./sphinx-key/rando.sh + +make your .env + +cargo run --bin config \ No newline at end of file diff --git a/crypter/src/chacha.rs b/crypter/src/chacha.rs index d0f0798..fd03189 100644 --- a/crypter/src/chacha.rs +++ b/crypter/src/chacha.rs @@ -1,13 +1,17 @@ -use lightning::util::chacha20poly1305rfc::ChaCha20Poly1305RFC; use anyhow::anyhow; +use lightning::util::chacha20poly1305rfc::ChaCha20Poly1305RFC; -pub const MSG_LEN: usize = 32; +pub const MSG_LEN: usize = 32; pub const KEY_LEN: usize = 32; pub const NONCE_END_LEN: usize = 8; -const TAG_LEN: usize = 16; -const CIPHER_LEN: usize = MSG_LEN + NONCE_END_LEN + TAG_LEN; +pub const TAG_LEN: usize = 16; +pub const CIPHER_LEN: usize = MSG_LEN + NONCE_END_LEN + TAG_LEN; -pub fn encrypt(plaintext: [u8; MSG_LEN], key: [u8; KEY_LEN], nonce_end: [u8; NONCE_END_LEN]) -> anyhow::Result<[u8; CIPHER_LEN]> { +pub fn encrypt( + plaintext: [u8; MSG_LEN], + key: [u8; KEY_LEN], + nonce_end: [u8; NONCE_END_LEN], +) -> anyhow::Result<[u8; CIPHER_LEN]> { let mut nonce = [0; 4 + NONCE_END_LEN]; nonce[4..].copy_from_slice(&nonce_end); let mut chacha = ChaCha20Poly1305RFC::new(&key, &nonce, &[0; 0]); @@ -38,7 +42,7 @@ pub fn decrypt(ciphertext: [u8; CIPHER_LEN], key: [u8; KEY_LEN]) -> anyhow::Resu #[cfg(test)] mod tests { - use crate::chacha::{decrypt, encrypt, MSG_LEN, KEY_LEN, NONCE_END_LEN}; + use crate::chacha::{decrypt, encrypt, KEY_LEN, MSG_LEN, NONCE_END_LEN}; use rand::{rngs::OsRng, RngCore}; #[test] @@ -52,5 +56,4 @@ mod tests { assert_eq!(plaintext, plain); Ok(()) } - -} \ No newline at end of file +} diff --git a/crypter/src/lib.rs b/crypter/src/lib.rs index 222b5ad..e81c883 100644 --- a/crypter/src/lib.rs +++ b/crypter/src/lib.rs @@ -7,7 +7,7 @@ pub use secp256k1; mod tests { use crate::chacha::{decrypt, encrypt, MSG_LEN, NONCE_END_LEN}; use crate::ecdh::derive_shared_secret_from_slice; - use rand::{rngs::OsRng, thread_rng, RngCore}; + use secp256k1::rand::{rngs::OsRng, thread_rng, RngCore}; use secp256k1::Secp256k1; #[test] diff --git a/sphinx-key/Cargo.toml b/sphinx-key/Cargo.toml index 8c1f798..8dc89c1 100644 --- a/sphinx-key/Cargo.toml +++ b/sphinx-key/Cargo.toml @@ -32,6 +32,7 @@ url = "2" serde_urlencoded = "0.7.1" serde = { version = "1.0.137", default-features = false } serde_json = { version = "1.0.81", default-features = false } +hex = "0.4.3" [patch.crates-io] # updates the "rand" create to use esp RNG diff --git a/sphinx-key/newseed.sh b/sphinx-key/newseed.sh new file mode 100755 index 0000000..4a41e14 --- /dev/null +++ b/sphinx-key/newseed.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +n=32 + +hexdump -vn "$n" -e ' /1 "%02x"' /dev/urandom ; echo \ No newline at end of file diff --git a/sphinx-key/run.sh b/sphinx-key/run.sh new file mode 100755 index 0000000..d447cd8 --- /dev/null +++ b/sphinx-key/run.sh @@ -0,0 +1,7 @@ +cargo +nightly build --release + +esptool.py --chip esp32c3 elf2image target/riscv32imc-esp-espidf/release/sphinx-key + +esptool.py --chip esp32c3 -p /dev/tty.usbserial-1420 -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 4MB 0x10000 target/riscv32imc-esp-espidf/release/sphinx-key.bin + +espmonitor /dev/tty.usbserial-1420 \ No newline at end of file diff --git a/sphinx-key/src/conn/http.rs b/sphinx-key/src/conn/http.rs index f21cc1c..3b92762 100644 --- a/sphinx-key/src/conn/http.rs +++ b/sphinx-key/src/conn/http.rs @@ -1,32 +1,64 @@ use crate::conn::html; -use crate::core::config::Config; +use crate::core::config::{Config, ConfigDTO}; +use serde::Deserialize; +use std::convert::TryInto; +use std::sync::{Arc, Condvar, Mutex}; + +use embedded_svc::httpd::registry::Registry; use embedded_svc::httpd::*; use esp_idf_svc::httpd as idf; -use std::sync::{Condvar, Mutex, Arc}; -use embedded_svc::httpd::registry::Registry; -use serde::Deserialize; + +use sphinx_key_crypter::chacha::{decrypt, CIPHER_LEN}; +use sphinx_key_crypter::ecdh::{derive_shared_secret_from_slice, PUBLIC_KEY_LEN}; +use sphinx_key_crypter::secp256k1::rand::thread_rng; +use sphinx_key_crypter::secp256k1::Secp256k1; +#[derive(Clone, Debug, Deserialize)] +pub struct Ecdh { + pub pubkey: String, +} #[derive(Clone, Debug, Deserialize)] pub struct Params { - pub config: String + pub config: String, } #[allow(unused_variables)] pub fn config_server(mutex: Arc<(Mutex>, Condvar)>) -> Result { + let s = Secp256k1::new(); + let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); + let pk_hex = hex::encode(pk1.serialize()); let server = idf::ServerRegistry::new() .at("/") .get(|_| Ok(html::HTML.into()))? + .at("/ecdh") + .get(move |_| Ok(format!("{{\"pubkey\":\"{}\"}}", pk_hex).to_owned().into()))? .at("/config") .post(move |request| { - let bod = &request.query_string() + let bod = &request + .query_string() .ok_or(anyhow::anyhow!("failed to parse query string"))?; println!("bod {:?}", bod); let params = serde_urlencoded::from_str::(bod)?; - let conf = serde_json::from_str::(¶ms.config)?; - + let dto = serde_json::from_str::(¶ms.config)?; + + let their_pk = hex::decode(dto.pubkey)?; + let their_pk_bytes: [u8; PUBLIC_KEY_LEN] = their_pk[..PUBLIC_KEY_LEN].try_into()?; + let shared_secret = + derive_shared_secret_from_slice(their_pk_bytes, sk1.secret_bytes())?; + // decrypt seed + let cipher_seed = hex::decode(dto.seed)?; + let cipher: [u8; CIPHER_LEN] = cipher_seed[..CIPHER_LEN].try_into()?; + let seed = decrypt(cipher, shared_secret)?; + + let conf = Config { + broker: dto.broker, + ssid: dto.ssid, + pass: dto.pass, + seed: seed, + }; let mut wait = mutex.0.lock().unwrap(); *wait = Some(conf); mutex.1.notify_one(); @@ -34,4 +66,4 @@ pub fn config_server(mutex: Arc<(Mutex>, Condvar)>) -> Result, config: &Config) -> Result> { - let wifi = conn::wifi::start_client( - default_nvs, - config - )?; +pub fn start_wifi_client(default_nvs: Arc, config: &Config) -> Result> { + let wifi = conn::wifi::start_client(default_nvs, config)?; println!("CLIENT CONNECTED!!!!!! {:?}", wifi.get_status()); Ok(wifi) } -pub fn start_config_server_and_wait(default_nvs: Arc) -> Result<(Box, Config)> { - +pub fn start_config_server_and_wait( + default_nvs: Arc, +) -> Result<(Box, Config)> { let mutex = Arc::new((Mutex::new(None), Condvar::new())); #[allow(clippy::redundant_clone)] #[allow(unused_mut)] - let mut wifi = conn::wifi::start_access_point( - default_nvs.clone(), - )?; + let mut wifi = conn::wifi::start_access_point(default_nvs.clone())?; let httpd = conn::http::config_server(mutex.clone()); - let mut wait = mutex.0.lock().unwrap(); let config: &Config = loop { @@ -67,4 +71,4 @@ pub fn start_config_server_and_wait(default_nvs: Arc) -> Result<( // thread::sleep(Duration::from_secs(1)); println!("===> config! {:?}", config); Ok((wifi, config.clone())) -} \ No newline at end of file +} diff --git a/tester/src/config.rs b/tester/src/config.rs index 61bb0c6..19984bb 100644 --- a/tester/src/config.rs +++ b/tester/src/config.rs @@ -21,6 +21,7 @@ pub struct ConfigBody { pub ssid: String, pub pass: String, pub broker: String, + pub pubkey: String, // for ecdh } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ConfigResponse { @@ -34,6 +35,9 @@ async fn main() -> anyhow::Result<()> { let ssid: String = env::var("SSID").expect("no ssid"); let pass: String = env::var("PASS").expect("no pass"); let broker: String = env::var("BROKER").expect("no broker"); + let seed_string: String = env::var("SEED").expect("no seed"); + let seed: [u8; MSG_LEN] = hex::decode(seed_string)?[..MSG_LEN].try_into()?; + println!("seed {:?}", seed); let s = Secp256k1::new(); let (sk1, pk1) = s.generate_keypair(&mut thread_rng()); @@ -43,12 +47,8 @@ async fn main() -> anyhow::Result<()> { .build() .expect("couldnt build reqwest client"); - let body = EcdhBody { - pubkey: hex::encode(pk1.serialize()), - }; let res = client - .post(format!("{}{}", URL, "ecdh")) - .json(&body) + .get(format!("{}{}", URL, "ecdh")) .header("Content-Type", "application/json") .send() .await?; @@ -58,22 +58,22 @@ async fn main() -> anyhow::Result<()> { let their_pk_bytes: [u8; PUBLIC_KEY_LEN] = their_pk[..PUBLIC_KEY_LEN].try_into()?; let shared_secret = derive_shared_secret_from_slice(their_pk_bytes, sk1.secret_bytes())?; - let plaintext = [1; MSG_LEN]; let mut nonce_end = [0; NONCE_END_LEN]; OsRng.fill_bytes(&mut nonce_end); - let cipher = encrypt(plaintext, shared_secret, nonce_end)?; + let cipher = encrypt(seed, shared_secret, nonce_end)?; let cipher_seed = hex::encode(cipher); let config = ConfigBody { seed: cipher_seed, ssid, pass, broker, + pubkey: hex::encode(pk1.serialize()), }; let conf_string = serde_json::to_string(&config)?; let conf_encoded = urlencoding::encode(&conf_string).to_owned(); let res2 = client - .post(format!("{}{}{}", URL, "/config?config=", conf_encoded)) + .post(format!("{}{}{}", URL, "config?config=", conf_encoded)) .send() .await?; let conf_res: ConfigResponse = res2.json().await?;