Merge pull request #43 from stakwork/feat/chacha20poly1305

Feat/chacha20poly1305
This commit is contained in:
Evan Feenstra
2022-07-05 15:14:20 -07:00
committed by GitHub
20 changed files with 462 additions and 105 deletions

3
.gitignore vendored
View File

@@ -5,4 +5,5 @@ Cargo.lock
.DS_Store
sphinx-key/Cargo.lock
notes.md
test-flash
test-flash
.env

View File

@@ -10,9 +10,11 @@ members = [
exclude = [
"sphinx-key",
"crypter"
]
[patch.crates-io]
# updates the "rand" create to use esp RNG
getrandom = { version = "0.2", git = "https://github.com/esp-rs-compat/getrandom.git" }
secp256k1 = { git = "https://github.com/Evanfeenstra/rust-secp256k1", branch = "v0.22.0-new-rand" }
lightning = { git = "https://github.com/Evanfeenstra/rust-lightning", branch = "v0.0.108-branch" }

View File

@@ -22,7 +22,9 @@ Find the path to your `riscv32-esp-elf-gcc` binary within the `.embuild` dir:
### flash release
`espflash target/riscv32imc-esp-espidf/release/sphinx-key --monitor`
`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`
### monitor
@@ -32,6 +34,26 @@ 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
NETWORK=regtest
```
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 +120,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

@@ -9,7 +9,6 @@ mod util;
use crate::chain_tracker::MqttSignerPort;
use crate::mqtt::start_broker;
use crate::unix_fd::SignerLoop;
use bitcoin::Network;
use clap::{arg, App, AppSettings, Arg};
use std::env;
use std::sync::Arc;
@@ -54,26 +53,9 @@ fn main() -> anyhow::Result<()> {
.arg(arg!(--"log-io" "ignored dev flag"))
.arg(arg!(--version "show a dummy version"))
.arg(arg!(--test "run a test against the embedded device"))
.arg(
Arg::new("network")
.help("bitcoin network")
.long("network")
.value_parser(["regtest", "signet", "testnet", "mainnet", "bitcoin"])
.default_value("regtest"),
);
let matches = app.get_matches();
let network_string: &String = matches.get_one("network").expect("expected a network");
let network: Network = match network_string.as_str() {
"bitcoin" => Network::Bitcoin,
"mainnet" => Network::Bitcoin,
"testnet" => Network::Testnet,
"signet" => Network::Signet,
"regtest" => Network::Regtest,
_ => Network::Regtest,
};
if matches.is_present("version") {
// Pretend to be the right version, given to us by an env var
let version =
@@ -82,7 +64,6 @@ fn main() -> anyhow::Result<()> {
return Ok(());
}
log::info!("NETWORK: {}", network.to_string());
if matches.is_present("test") {
run_test::run_test();
return Ok(());
@@ -98,8 +79,6 @@ fn main() -> anyhow::Result<()> {
log::info!("=> connection status: {}", status);
assert_eq!(status, true, "expected connected = true");
// runtime.block_on(async {
init::blocking_connect(tx.clone(), network);
log::info!("=====> sent seed!");
if let Ok(btc_url) = env::var("BITCOIND_RPC_URL") {
let signer_port = MqttSignerPort::new(tx.clone());

23
crypter/Cargo.toml Normal file
View File

@@ -0,0 +1,23 @@
[package]
name = "sphinx-key-crypter"
version = "0.1.0"
authors = ["Evan Feenstra <evanfeenstra@gmail.com>"]
edition = "2018"
[dependencies]
rand = "0.8"
anyhow = {version = "1", features = ["backtrace"]}
log = "0.4"
base64 = { version = "0.13.0" }
secp256k1 = { version = "0.22.0", features = ["std", "rand-std"] }
[dependencies.lightning]
version = "0.0.108"
default-features = false
features = ["std", "grind_signatures"]
[patch.crates-io]
getrandom = { version = "0.2", git = "https://github.com/esp-rs-compat/getrandom.git" }
secp256k1 = { git = "https://github.com/Evanfeenstra/rust-secp256k1", branch = "v0.22.0-new-rand" }
lightning = { git = "https://github.com/Evanfeenstra/rust-lightning", branch = "v0.0.108-branch" }

59
crypter/src/chacha.rs Normal file
View File

@@ -0,0 +1,59 @@
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;
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]> {
let mut nonce = [0; 4 + NONCE_END_LEN];
nonce[4..].copy_from_slice(&nonce_end);
let mut chacha = ChaCha20Poly1305RFC::new(&key, &nonce, &[0; 0]);
let mut res = [0; MSG_LEN];
let mut tag = [0; TAG_LEN];
chacha.encrypt(&plaintext[..], &mut res[0..plaintext.len()], &mut tag);
let mut ret = [0; CIPHER_LEN];
ret[..MSG_LEN].copy_from_slice(&res);
ret[MSG_LEN..MSG_LEN + NONCE_END_LEN].copy_from_slice(&nonce_end);
ret[MSG_LEN + NONCE_END_LEN..].copy_from_slice(&tag);
Ok(ret)
}
pub fn decrypt(ciphertext: [u8; CIPHER_LEN], key: [u8; KEY_LEN]) -> anyhow::Result<[u8; MSG_LEN]> {
let mut nonce = [0; 4 + NONCE_END_LEN];
nonce[4..].copy_from_slice(&ciphertext[MSG_LEN..MSG_LEN + NONCE_END_LEN]);
let mut tag = [0; TAG_LEN];
tag.copy_from_slice(&ciphertext[MSG_LEN + NONCE_END_LEN..]);
let mut chacha2 = ChaCha20Poly1305RFC::new(&key, &nonce, &[0; 0]);
let mut dec = [0; MSG_LEN];
let ok = chacha2.decrypt(&ciphertext[..MSG_LEN], &mut dec, &tag);
if ok {
Ok(dec)
} else {
Err(anyhow!("failed chacha authentication"))
}
}
#[cfg(test)]
mod tests {
use crate::chacha::{decrypt, encrypt, KEY_LEN, MSG_LEN, NONCE_END_LEN};
use rand::{rngs::OsRng, RngCore};
#[test]
fn test_chacha() -> anyhow::Result<()> {
let key = [9; KEY_LEN];
let plaintext = [1; MSG_LEN];
let mut nonce_end = [0; NONCE_END_LEN];
OsRng.fill_bytes(&mut nonce_end);
let cipher = encrypt(plaintext, key, nonce_end)?;
let plain = decrypt(cipher, key)?;
assert_eq!(plaintext, plain);
Ok(())
}
}

47
crypter/src/ecdh.rs Normal file
View File

@@ -0,0 +1,47 @@
use secp256k1::ecdh::SharedSecret;
use secp256k1::{SecretKey, PublicKey};
use anyhow::Result;
pub const PUBLIC_KEY_LEN: usize = 33;
pub const PRIVATE_KEY_LEN: usize = 32;
pub const SECRET_LEN: usize = 32;
pub fn derive_shared_secret_from_slice(their_public_key: [u8; PUBLIC_KEY_LEN], my_private_key: [u8; PRIVATE_KEY_LEN]) -> Result<[u8; SECRET_LEN]> {
let public_key = PublicKey::from_slice(&their_public_key[..])?;
let private_key = SecretKey::from_slice(&my_private_key[..])?;
Ok(derive_shared_secret(&public_key, &private_key).secret_bytes())
}
pub fn derive_shared_secret(their_public_key: &PublicKey, my_private_key: &SecretKey) -> SharedSecret {
SharedSecret::new(their_public_key, my_private_key)
}
#[cfg(test)]
mod tests {
use crate::ecdh::{derive_shared_secret, derive_shared_secret_from_slice};
use rand::thread_rng;
use secp256k1::Secp256k1;
#[test]
fn test_ecdh() -> anyhow::Result<()> {
let s = Secp256k1::new();
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
let (sk2, pk2) = s.generate_keypair(&mut thread_rng());
let sec1 = derive_shared_secret(&pk2, &sk1);
let sec2 = derive_shared_secret(&pk1, &sk2);
assert_eq!(sec1, sec2);
Ok(())
}
#[test]
fn test_ecdh_from_slice() -> anyhow::Result<()> {
let s = Secp256k1::new();
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
let (sk2, pk2) = s.generate_keypair(&mut thread_rng());
let sec1 = derive_shared_secret_from_slice(pk2.serialize(), sk1.secret_bytes())?;
let sec2 = derive_shared_secret_from_slice(pk1.serialize(), sk2.secret_bytes())?;
assert_eq!(sec1, sec2);
Ok(())
}
}

38
crypter/src/lib.rs Normal file
View File

@@ -0,0 +1,38 @@
pub mod chacha;
pub mod ecdh;
pub use secp256k1;
#[cfg(test)]
mod tests {
use crate::chacha::{decrypt, encrypt, MSG_LEN, NONCE_END_LEN};
use crate::ecdh::derive_shared_secret_from_slice;
use secp256k1::rand::{rngs::OsRng, thread_rng, RngCore};
use secp256k1::Secp256k1;
#[test]
fn test_crypter() -> anyhow::Result<()> {
// two keypairs
let s = Secp256k1::new();
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
let (sk2, pk2) = s.generate_keypair(&mut thread_rng());
// derive shared secrets
let sec1 = derive_shared_secret_from_slice(pk2.serialize(), sk1.secret_bytes())?;
let sec2 = derive_shared_secret_from_slice(pk1.serialize(), sk2.secret_bytes())?;
assert_eq!(sec1, sec2);
// encrypt plaintext with sec1
let plaintext = [1; MSG_LEN];
let mut nonce_end = [0; NONCE_END_LEN];
OsRng.fill_bytes(&mut nonce_end);
let cipher = encrypt(plaintext, sec1, nonce_end)?;
// decrypt with sec2
let plain = decrypt(cipher, sec2)?;
assert_eq!(plaintext, plain);
println!("PLAINTEXT MATCHES!");
Ok(())
}
}

View File

@@ -1,14 +1,15 @@
use lightning_signer::persist::{DummyPersister, Persist};
// use lightning_signer::Arc;
use sphinx_key_parser::MsgDriver;
use std::sync::Arc;
use vls_protocol::model::PubKey;
use vls_protocol::msgs::{self, read_serial_request_header, write_serial_response_header, Message};
use vls_protocol::serde_bolt::WireString;
use vls_protocol_signer::handler::{Handler, RootHandler};
pub use vls_protocol_signer::lightning_signer;
pub use vls_protocol_signer::lightning_signer::bitcoin::Network;
pub use vls_protocol_signer::vls_protocol;
pub use sphinx_key_parser::MsgDriver;
pub struct InitResponse {
pub root_handler: RootHandler,

View File

@@ -21,6 +21,7 @@ pingpong = []
[dependencies]
esp-idf-sys = { version = "0.31.5", features = ["binstart"] }
sphinx-key-signer = { path = "../signer", optional = true }
sphinx-key-crypter = { path = "../crypter" }
embedded-svc = { version = "0.21.2" }
esp-idf-svc = "0.41"
esp-idf-hal = "0.37"
@@ -31,11 +32,13 @@ 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
getrandom = { version = "0.2", git = "https://github.com/esp-rs-compat/getrandom.git" }
secp256k1 = { git = "https://github.com/Evanfeenstra/rust-secp256k1", branch = "v0.22.0-new-rand" }
lightning = { git = "https://github.com/Evanfeenstra/rust-lightning", branch = "v0.0.108-branch" }
[build-dependencies]
embuild = "0.29"

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,65 @@
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,
network: dto.network,
seed: seed,
};
let mut wait = mutex.0.lock().unwrap();
*wait = Some(conf);
mutex.1.notify_one();
@@ -34,4 +67,4 @@ pub fn config_server(mutex: Arc<(Mutex<Option<Config>>, Condvar)>) -> Result<idf
})?;
server.start(&Default::default())
}
}

View File

@@ -1,19 +1,30 @@
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],
pub network: String,
}
#[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)
pub network: String,
}
/*
@@ -27,27 +38,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 {
@@ -67,4 +73,4 @@ pub fn start_config_server_and_wait(default_nvs: Arc<EspDefaultNvs>) -> Result<(
// thread::sleep(Duration::from_secs(1));
println!("===> config! {:?}", config);
Ok((wifi, config.clone()))
}
}

View File

@@ -1,4 +1,6 @@
use crate::conn::mqtt::{QOS, RETURN_TOPIC, TOPIC};
use crate::core::init::make_init_msg;
use sphinx_key_signer::vls_protocol::model::PubKey;
use sphinx_key_signer::{self, InitResponse};
use sphinx_key_signer::lightning_signer::bitcoin::Network;
@@ -36,35 +38,35 @@ pub fn make_event_loop(
rx: mpsc::Receiver<Event>,
network: Network,
do_log: bool,
led_tx: mpsc::Sender<Status>
led_tx: mpsc::Sender<Status>,
seed: [u8; 32]
) -> Result<()> {
// initialize the RootHandler
let root_handler = loop {
if let Ok(event) = rx.recv() {
match event {
Event::Connected => {
log::info!("SUBSCRIBE to {}", TOPIC);
mqtt.subscribe(TOPIC, QOS)
.expect("could not MQTT subscribe");
led_tx.send(Status::Connected).unwrap();
}
Event::Message(ref msg_bytes) => {
let InitResponse {
root_handler,
init_reply,
} = sphinx_key_signer::init(msg_bytes.clone(), network).expect("failed to init signer");
mqtt.publish(RETURN_TOPIC, QOS, false, init_reply)
.expect("could not publish init response");
break root_handler;
}
Event::Disconnected => {
led_tx.send(Status::ConnectingToMqtt).unwrap();
log::info!("GOT an early Event::Disconnected msg!");
}
while let Ok(event) = rx.recv() {
match event {
Event::Connected => {
log::info!("SUBSCRIBE to {}", TOPIC);
mqtt.subscribe(TOPIC, QOS)
.expect("could not MQTT subscribe");
led_tx.send(Status::Connected).unwrap();
break;
}
Event::Message(ref _msg_bytes) => {
panic!("should not be a message before connection");
}
Event::Disconnected => {
led_tx.send(Status::ConnectingToMqtt).unwrap();
log::info!("GOT an early Event::Disconnected msg!");
}
}
};
}
// initialize the RootHandler
let init_msg = make_init_msg(network, seed).expect("failed to make init msg");
let InitResponse {
root_handler,
init_reply: _,
} = sphinx_key_signer::init(init_msg, network).expect("failed to init signer");
// signing loop
let dummy_peer = PubKey([0; 33]);
while let Ok(event) = rx.recv() {
@@ -105,7 +107,8 @@ pub fn make_event_loop(
rx: mpsc::Receiver<Event>,
_network: Network,
do_log: bool,
led_tx: mpsc::Sender<Status>
led_tx: mpsc::Sender<Status>,
_seed: [u8; 32]
) -> Result<()> {
log::info!("About to subscribe to the mpsc channel");
while let Ok(event) = rx.recv() {

View File

@@ -0,0 +1,20 @@
use sphinx_key_signer::MsgDriver;
use sphinx_key_signer::vls_protocol::model::Secret;
use sphinx_key_signer::vls_protocol::{msgs, serde_bolt::WireString};
use sphinx_key_signer::lightning_signer::bitcoin::Network;
pub fn make_init_msg(network: Network, seed: [u8; 32]) -> anyhow::Result<Vec<u8>> {
let allowlist = Vec::new();
log::info!("allowlist {:?} seed {:?}", allowlist, seed);
let init = msgs::HsmdInit2 {
derivation_style: 0,
network_name: WireString(network.to_string().as_bytes().to_vec()),
dev_seed: Some(Secret(seed)),
dev_allowlist: allowlist,
};
let sequence = 0;
let mut md = MsgDriver::new_empty();
msgs::write_serial_request_header(&mut md, sequence, 0)?;
msgs::write(&mut md, init)?;
Ok(md.bytes())
}

View File

@@ -1,2 +1,3 @@
pub mod events;
pub mod config;
pub mod init;

View File

@@ -3,20 +3,20 @@ mod conn;
mod core;
mod periph;
use crate::core::{events::*, config::*};
use crate::core::{config::*, events::*};
use crate::periph::led::led_control_loop;
use crate::periph::sd::sd_card;
use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use std::thread;
use std::sync::{Arc, mpsc};
use std::time::Duration;
use anyhow::Result;
use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use std::sync::{mpsc, Arc};
use std::thread;
use std::time::Duration;
use esp_idf_svc::nvs::*;
use esp_idf_svc::nvs_storage::EspNvsStorage;
use embedded_svc::storage::Storage;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_svc::nvs::*;
use esp_idf_svc::nvs_storage::EspNvsStorage;
use sphinx_key_signer::lightning_signer::bitcoin::Network;
@@ -26,31 +26,14 @@ const CLIENT_ID: &str = "sphinx-1";
#[cfg(feature = "pingpong")]
const CLIENT_ID: &str = "test-1";
const NETWORK: Option<&'static str> = option_env!("NETWORK");
fn main() -> Result<()> {
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();
let network: Network = if let Some(n) = NETWORK {
match n {
"bitcoin" => Network::Bitcoin,
"mainnet" => Network::Bitcoin,
"testnet" => Network::Testnet,
"signet" => Network::Signet,
"regtest" => Network::Regtest,
_ => Network::Regtest,
}
} else {
Network::Regtest
};
esp_idf_svc::log::EspLogger::initialize_default();
thread::sleep(Duration::from_secs(1));
log::info!("Network set to {:?}", network);
let peripherals = Peripherals::take().unwrap();
let pins = peripherals.pins;
@@ -63,10 +46,14 @@ fn main() -> Result<()> {
sd_card(peripherals.spi2);
let default_nvs = Arc::new(EspDefaultNvs::new()?);
let mut store = EspNvsStorage::new_default(default_nvs.clone(), "sphinx", true).expect("no storage");
let mut store =
EspNvsStorage::new_default(default_nvs.clone(), "sphinx", true).expect("no storage");
let existing: Option<Config> = store.get("config").expect("failed");
if let Some(exist) = existing {
println!("=============> START CLIENT NOW <============== {:?}", exist);
println!(
"=============> START CLIENT NOW <============== {:?}",
exist
);
// store.remove("config").expect("couldnt remove config");
led_tx.send(Status::ConnectingToWifi).unwrap();
let _wifi = start_wifi_client(default_nvs.clone(), &exist)?;
@@ -77,17 +64,26 @@ fn main() -> Result<()> {
// _conn needs to stay in scope or its dropped
let (mqtt, connection) = conn::mqtt::make_client(&exist.broker, CLIENT_ID)?;
let mqtt_client = conn::mqtt::start_listening(mqtt, connection, tx)?;
// this blocks forever... the "main thread"
log::info!(">>>>>>>>>>> blocking forever...");
let do_log = true;
make_event_loop(mqtt_client, rx, network, do_log, led_tx)?;
let network = match exist.network.as_str() {
"bitcoin" => Network::Bitcoin,
"mainnet" => Network::Bitcoin,
"testnet" => Network::Testnet,
"signet" => Network::Signet,
"regtest" => Network::Regtest,
_ => Network::Regtest,
};
log::info!("Network set to {:?}", network);
log::info!(">>>>>>>>>>> blocking forever...");
make_event_loop(mqtt_client, rx, network, do_log, led_tx, exist.seed)?;
} else {
led_tx.send(Status::WifiAccessPoint).unwrap();
println!("=============> START SERVER NOW AND WAIT <==============");
if let Ok((wifi, config)) = start_config_server_and_wait(default_nvs.clone()) {
store.put("config", &config).expect("could not store config");
store
.put("config", &config)
.expect("could not store config");
println!("CONFIG SAVED");
drop(wifi);
thread::sleep(Duration::from_secs(1));

View File

@@ -7,6 +7,7 @@ edition = "2018"
[dependencies]
sphinx-key-signer = { path = "../signer" }
sphinx-key-parser = { path = "../parser" }
sphinx-key-crypter = { path = "../crypter" }
vls-protocol = { git = "https://gitlab.com/Evanfeenstra/validating-lightning-signer", branch = "partial-std" }
vls-protocol-signer = { git = "https://gitlab.com/Evanfeenstra/validating-lightning-signer", branch = "partial-std", default-features = false, features = ["std", "secp-lowmemory"] }
# vls-protocol = { path = "../../../evanf/validating-lightning-signer/vls-protocol" }
@@ -20,3 +21,14 @@ clap = "3.2.6"
clap_derive = "3.2.6"
fern = { version = "0.6", features = ["colored"] }
chrono = "0.4"
rand = "0.8"
reqwest = { version = "0.11.11", features = ["json"] }
hex = "0.4.3"
serde = { version = "1.0.101", features = ["derive"] }
serde_json = "1.0"
urlencoding = "2.1.0"
dotenv = "0.15.0"
[[bin]]
name = "config"
path = "src/config.rs"

91
tester/src/config.rs Normal file
View File

@@ -0,0 +1,91 @@
use rand::{rngs::OsRng, thread_rng, RngCore};
use serde::{Deserialize, Serialize};
use sphinx_key_crypter::chacha::{encrypt, MSG_LEN, NONCE_END_LEN};
use sphinx_key_crypter::ecdh::{derive_shared_secret_from_slice, PUBLIC_KEY_LEN};
use sphinx_key_crypter::secp256k1::Secp256k1;
use std::convert::TryInto;
use std::time::Duration;
use dotenv::dotenv;
use std::env;
const URL: &str = "http://192.168.71.1/";
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EcdhBody {
pub pubkey: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ConfigBody {
pub seed: String,
pub ssid: String,
pub pass: String,
pub broker: String,
pub pubkey: String, // for ecdh
pub network: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ConfigResponse {
pub success: bool,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv().ok();
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()?;
let network: String = env::var("NETWORK").unwrap_or("regtest".to_string());
if !(network == "bitcoin" || network == "mainnet" || network == "testnet" || network == "signet" || network == "regtest") {
panic!("invalid network string");
}
println!("network {:?}", network);
let s = Secp256k1::new();
let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()
.expect("couldnt build reqwest client");
let res = client
.get(format!("{}{}", URL, "ecdh"))
.header("Content-Type", "application/json")
.send()
.await?;
let their_ecdh: EcdhBody = res.json().await?;
let their_pk = hex::decode(their_ecdh.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())?;
let mut nonce_end = [0; NONCE_END_LEN];
OsRng.fill_bytes(&mut 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, network,
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))
.send()
.await?;
let conf_res: ConfigResponse = res2.json().await?;
if conf_res.success {
println!("SUCCESS!")
}
Ok(())
}