mirror of
https://github.com/stakwork/sphinx-key.git
synced 2026-02-23 08:34:29 +01:00
Merge pull request #43 from stakwork/feat/chacha20poly1305
Feat/chacha20poly1305
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,4 +5,5 @@ Cargo.lock
|
||||
.DS_Store
|
||||
sphinx-key/Cargo.lock
|
||||
notes.md
|
||||
test-flash
|
||||
test-flash
|
||||
.env
|
||||
@@ -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" }
|
||||
|
||||
32
README.md
32
README.md
@@ -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
|
||||
@@ -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
23
crypter/Cargo.toml
Normal 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
59
crypter/src/chacha.rs
Normal 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
47
crypter/src/ecdh.rs
Normal 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
38
crypter/src/lib.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
5
sphinx-key/newseed.sh
Executable 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
7
sphinx-key/run.sh
Executable 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
|
||||
@@ -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>(¶ms.config)?;
|
||||
|
||||
let dto = serde_json::from_str::<ConfigDTO>(¶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,
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
20
sphinx-key/src/core/init.rs
Normal file
20
sphinx-key/src/core/init.rs
Normal 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())
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod events;
|
||||
pub mod config;
|
||||
pub mod init;
|
||||
@@ -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));
|
||||
|
||||
@@ -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
91
tester/src/config.rs
Normal 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(())
|
||||
}
|
||||
Reference in New Issue
Block a user