encrypted seed to hardware, configDTO, tested config bin that takes params from .env

This commit is contained in:
Evan Feenstra
2022-07-01 13:19:11 -07:00
parent c8e6e834e9
commit 5bb4a990d2
9 changed files with 120 additions and 41 deletions

View File

@@ -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

View File

@@ -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 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(())
}
}

View File

@@ -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]

View File

@@ -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

5
sphinx-key/newseed.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
n=32
hexdump -vn "$n" -e ' /1 "%02x"' /dev/urandom ; echo

7
sphinx-key/run.sh Executable file
View File

@@ -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

View File

@@ -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<Option<Config>>, Condvar)>) -> Result<idf::Server> {
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::<Params>(bod)?;
let conf = serde_json::from_str::<Config>(&params.config)?;
let dto = serde_json::from_str::<ConfigDTO>(&params.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();

View File

@@ -1,19 +1,28 @@
use crate::conn;
use anyhow::Result;
use std::sync::{Condvar, Mutex, Arc};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
use serde::{Serialize, Deserialize};
use embedded_svc::wifi::*;
use esp_idf_svc::nvs::*;
use esp_idf_svc::wifi::*;
use embedded_svc::wifi::*;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Config {
pub broker: String,
pub ssid: String,
pub pass: String,
pub seed: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ConfigDTO {
pub broker: String,
pub ssid: String,
pub pass: String,
pub pubkey: String,
pub seed: String, // encrypted (56 bytes)
}
/*
@@ -27,27 +36,22 @@ http://192.168.71.1/?broker=192.168.86.222%3A1883
*/
pub fn start_wifi_client(default_nvs: Arc<EspDefaultNvs>, config: &Config) -> Result<Box<EspWifi>> {
let wifi = conn::wifi::start_client(
default_nvs,
config
)?;
pub fn start_wifi_client(default_nvs: Arc<EspDefaultNvs>, config: &Config) -> Result<Box<EspWifi>> {
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<EspDefaultNvs>) -> Result<(Box<EspWifi>, Config)> {
pub fn start_config_server_and_wait(
default_nvs: Arc<EspDefaultNvs>,
) -> Result<(Box<EspWifi>, 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 {

View File

@@ -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?;