forked from openprivacy/niwl
Fix Heartbeat + a few more notes on metadata + security warnings and examples
This commit is contained in:
parent
c2a3518e96
commit
12f6b705cb
|
@ -1080,6 +1080,7 @@ dependencies = [
|
|||
"fuzzytags",
|
||||
"hex",
|
||||
"niwl",
|
||||
"rand 0.8.3",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
74
README.md
74
README.md
|
@ -8,6 +8,17 @@ event has happened e.g. a new group message, a payment etc.
|
|||
**niwl** provides a set of libraries, clients and servers to provide this in a metadata resistant, bandwidth
|
||||
efficient way based on [fuzzytags](https://crates.io/crates/fuzzytags).
|
||||
|
||||
## Security (hic sunt dracones)
|
||||
|
||||
This crate workspace provides and documents a novel and highly experimental metadata resistant communication system.
|
||||
|
||||
The code has not undergone any significant review.
|
||||
|
||||
Further, it is based on an [experimental implementation (fuzzytags), of an experimental cryptographic scheme (FMD2)](https://git.openprivacy.ca/openprivacy/fuzzytags)
|
||||
which also has a large list of security warnings.
|
||||
|
||||
I urge you to not rely on this code or derivative systems until it has been reviewed and given considerable thought.
|
||||
|
||||
# How Niwl Works
|
||||
|
||||
A Niwl system relies on a single, untrusted routing server that acts as a bulletin board.
|
||||
|
@ -69,24 +80,40 @@ This broad genre of attacks can be generalized as follows:
|
|||
1. REMs start with a pool of randomly generated messages, this protected initial messages sent to the REM.
|
||||
2. Over time this pool is probabilistically replaced by messages from the network.
|
||||
3. A malicious Niwl server, having identified a REM, can flood the REM with its own messages.
|
||||
4. At a certain number of messages, the probability that a REM store contains only messages from the Niwl server approaches 1.0.
|
||||
4. At a certain number of messages, the probability that a REM store contains only messages from the niwl server approaches 1.0.
|
||||
5. A Niwl server can then delay every other message sent to it by other clients one-by-one.
|
||||
1. If the message isn't for the REM then nothing will happen.
|
||||
2. If the message is for the REM then the REM will either eject a message known to the Niwl Server, or it will eject
|
||||
an unknown message than the Niwl Server can then correlate with a Sender and a set of Receivers.
|
||||
|
||||
Before diving into mitigation strategies it is worth outlining a few properties of Niwl that differ from other
|
||||
mixing-based anonymity systems.
|
||||
|
||||
First, we should note that Niwl is less prone to these kinds of attacks because:
|
||||
0. Using REMs are not mandatory; parties may exchange messages with each other directly. Doing so does introduce a vulnerability
|
||||
to statistical analysis.
|
||||
1. Different parties can rely on different REMs without compromising metadata privacy, and without negotiation.
|
||||
2. If a REM becomes slow to respond or sends out and error alert, parties may choose to move to a different REM.
|
||||
3. Different REMs can adopt different mixing strategies, and may be selective about what traffic they mix.
|
||||
|
||||
1. REMs are not, a-priori, known to the Niwl Server and such are more difficult to target than mixers in traditional mixnets.
|
||||
2. Different parties can rely on different REMs without compromising metadata privacy.
|
||||
Additionally, we should also enumerate what could go wrong, in addition to an active attack on a particular mix.
|
||||
|
||||
As such targeting a particular mix is not an effective strategy for undermining the anonymity set of the entire system.
|
||||
The niwl server may deliberately drop or delay packets arbitrarily. Beyond this prototype it is worth considering
|
||||
incentive mechanisms such as ([token-based services](https://openprivacy.ca/research/OPTR2019-01/)) to mitigate this.
|
||||
|
||||
Further, REMs employ [heartbeat messages](references/heartbeat.pdf) (messages periodically sent to the Niwl server addressed to the REM)
|
||||
to detect such attacks. If a REM does not receive its own heartbeat message shortly after it is sent, it begins injecting random messages
|
||||
into its pool to thwart mixers. It can also display this status publicly and/or include the status in legitimate messages alerting
|
||||
other clients to the malicious Niwl Server
|
||||
Niwl servers may attempt to passively profile traffic originating from clients in an attempt to determine mixing nodes.
|
||||
REMs always download all messages from the niwl server and so the only available metadata exposed is the rate at which
|
||||
a REM *sends* messages. This can be partially mitigated by introducing random delays between individual sends, and between
|
||||
syncing periods.
|
||||
|
||||
REMs employ [heartbeat messages](references/heartbeat.pdf) (messages periodically sent to the Niwl server addressed to the REM)
|
||||
to detect such attacks. If a REM does not receive its own heartbeat message shortly after it is sent, it begins injecting random messages
|
||||
into its pool to thwart mixers. It can also display this status publicly and/or include the status in legitimate messages alerting
|
||||
other clients to the malicious Niwl Server.
|
||||
|
||||
The rate at which a niwl sends out a heartbeat message is also a vector for passive profiling. Heartbeats must not
|
||||
be distinguishable from other niwl traffic through their rate.
|
||||
|
||||
Finally, the fact that a REM
|
||||
|
||||
# Code Overview
|
||||
|
||||
|
@ -101,8 +128,9 @@ for new tags.
|
|||
|
||||
For a more detailed overview please check out each individual crate.
|
||||
|
||||
# Example
|
||||
# Examples
|
||||
|
||||
## Simple Peer-to-Peer
|
||||
|
||||
niwl-client alice.profile generate "alice"
|
||||
Tagging Key: auaaaaaaaaaaaylmnfrwkgaaaaaaaaaaadlbii3y7r6vmc7upbxa4myohaqmr5xl22bdxeed4abkotnovlmakzdo5stq2ibtjewm4rnkgzqwglrt72zfeyomvdpxqnu4ci4hwebyiseyn7pqfxypnvef7a3flu2hby7gdluh6wocxa5mvmimi2xorydcqaca2p2aevmue4cwyxnw2h7fkps7e6grgls66zgohbnwjibt6nlsdqjbrdjrzlsc3at3f43jyniz2i67ng6xdty5pr3elzedhjlvefhd6pjfc7g4owrz3dkq5xt2hhh3vvctkywqkcwriguayyx3pourepfs7s76bekrjgcjgj6zyid3ixmeh5ewqhkhxhzevf3uogvscxtpbksaclhccht7pj2fungnztfghshd6lsmegmysiiuyav6schtmyxmne2vfi4j4cxllm2crj3cqofsxjlxov3ms2zgtjzyxtubwtnwspc4jhijz4kufm6r3qkhpcyibx7ulceckx2a4g23tkhtgshtxq3fga7ptbhq5gcebwiq6cfolt4zbn72gbmtc43nw63vd4soxf4bnbhrykaoudfs3mh6laap6iwbngo4ylocs4w5hgd4t22yrtrmhkewsc2eytsosxyhaiuaww24mszscsojm2bcoldpokwuxbnfx7lgnzdcuae3y55zoen47noltjqgcpuqzl6upjcvutgvvro6nu2uyl36rcqmw2by2e45uqtsdnolbispxv2e5aeeuz5gytuf5f5e44nldmywtmxkfqfljml5gye6tj3qswmz6d36f2k4v7fbiuv7jzplzmghsgxvmq7fo3qp655obysbggkd3iqpk76p5umbpc2tk64oiklrponulkqf3v337aaxyn6nvzz2rpj3o374tftscsr7oilzkah63xpe2jc45dd4fuwxvlg3c33zgkminemqqfz7jdjtnawy77vpxxgnosbw4fwadhhggofmipboiqo55xygojdnfdkuzgfe4455sdqv5ytzdl55yuzlbdgsnwtgnfakmoyjhblzbuwohq7esayfxe72yqgci5dappiad7bc3ikfsydv5b7stifajkxuosu345upxg2hwzajj4uu7lxaykxgo22pslkxnidaoyevn3gamx63ec4fkhzguhbu6jt7pukr4rpafx24vd622f5wzux4corlxthjuhi2ewiu6laxx3aqfkzv2d2hhqzsac25vycmmxy
|
||||
|
@ -115,9 +143,33 @@ For a more detailed overview please check out each individual crate.
|
|||
niwl-client alice.profile tag-and-send bob
|
||||
Tag for bob 7e441275a5c3f88606c34c3451a44eaeaa025680cfcb3d9db53992501cc22134 4f7a7f961bc19297fee98da5f8601aa8373429b80b10c55dbe8116aa8c497a0e 71d8da
|
||||
|
||||
niwl-client bob.profile detect 10
|
||||
niwl-client bob.profile detect
|
||||
7e441275a5c3f88606c34c3451a44eaeaa025680cfcb3d9db53992501cc22134 4f7a7f961bc19297fee98da5f8601aa8373429b80b10c55dbe8116aa8c497a0e 71d8da
|
||||
|
||||
## Mix and Send
|
||||
|
||||
// Create a mixer
|
||||
niwl-rem generate mixer
|
||||
<!--- snip key -->
|
||||
niwl-rem run
|
||||
[DEBUG] kicking off initial heartbeat...
|
||||
.....
|
||||
|
||||
|
||||
// Alice imports a keyset for a mixer and sends a message to bob via the mixer using `tag-and-mix`
|
||||
niwl-client alice.niwl import-tagging-key auaaaaaaaaaaa3ljpbsxegaaaaaaaaaaabeiwbh2iiojfcurszypf4urscr5p7s6q7dzoeqyamrtx63hoakgogb7azd3ov37hippyqdar4povsf7oq25zogfr4qjabgzqcxedttrfceqffywwubechylxd4qzouedzkkhyg2f6e6aftdypvfoff6345li5hmfetjja6aswyffb5ngohin2cdg5qokko7s4kb7d7hb33ki6uuaenxi7neuden2fxxys3dczicfacw3iwhqw7kygs67kzre7tljrfktss4whzhurmozs4znyrnnjmzazsbrijl2fmcatc3v6ptxdpw35vt6zwnyz2l7fcpblmtrnthmrmxiej3hcvi5d7qiwj6s7wi2dygu2ref2o5jm2tug3lxgbbgqwsqvoo7d5eropddbkhcbr5pzls5nco5hkpnuubho54i4msm7kinzobnc5rgyduo2dpl6jo6pnlb7nckmpcgmcyrntg52xmvzhbxumrtiwvhxaqdscsrvgz7eg5szngetzsitpdfhycskxmnxwe6himyllywdxzalojuit5ap5ugfsmcmywn5hciwupx2y2asgjxowmhjhiubjph6b6y7jiuyqnyjjrwehotass4432hrilxzxzrmppdbt2yo3kfmdtxv5fseyp2k7ld2gr7z5ds3fxc5mtilvj3fzaw5tabhxtf73uykozbgjimzs7cfluhcmwitytjdw72r3ws552fjre6pq5jwx2ihd5u2odegvhq7wuqg5xmjvmayirqywobsdkm7szk7r5n4svoareaomq3cmmxwpv45ftfnp2adzmcb4bqzwvvwfsjjfsmepb7ocyw6bgy6hh7cugfafv6ww3pukhzydemisv67r4wbeoyhdebx2mp22wjcyqzcsa66k3k236uz7v3sf7n5577td52zjwiu27wiugehvymi3nnfm5qx3ps7ts7qkp6y2qqf4rdyg3z23oswhw6ku2nxlniesc4u6nhcg5h3olcrrbh4c3q3nyejnbs2msyxxuasofm6ayn5fl5rhomr74jzmp35xfzw7mu6uwciwbcommq733d3cvtpwhetcqjoxpbrydcdrhvux43ybbauc6aqkwlpoid7cfrycexadbe3ilmzlinpppr43k7y6cj3rewsu42gb5ici5a3sy7mk66xudceu3novaxdtscgucz3yy3jp26ovqqdmvedmgk33p6puqguhdwwuxqz6u5jyhjvxllg7w55xpptj64dphamnix3wxcjimginb7d2k7qz6ey
|
||||
Got: mixer: aa18c8597fd54a7779a0770c15ecbcc4d247009c007425172ba560b17f180516
|
||||
niwl-client alice.niwl tag-and-mix mixer bob "Hello Mixnet"
|
||||
|
||||
// Bob should receive the message some time later.
|
||||
niwl-client bob.niwl detect
|
||||
message: Hello Mixnet
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- Thanks to Erinn Atwater for helpful discussions.
|
||||
- FuzzyTags is based on [Fuzzy Message Detection](https://eprint.iacr.org/2021/089) by Gabrielle Beck and Julia Len and Ian Miers and Matthew Green
|
||||
|
||||
## References
|
||||
|
||||
* Danezis, George, and Len Sassaman. "Heartbeat traffic to counter (n-1) attacks: red-green-black mixes." Proceedings of the 2003 ACM workshop on Privacy in the electronic society. 2003.
|
|
@ -16,4 +16,5 @@ hex = "0.4.2"
|
|||
base32 = "0.4.0"
|
||||
reqwest = {version="0.11.0", features=["json"]}
|
||||
tokio = "1.2.0"
|
||||
chrono = {version="0.4.19", features=["serde"]}
|
||||
chrono = {version="0.4.19", features=["serde"]}
|
||||
rand = "0.8.3"
|
|
@ -14,11 +14,15 @@ pub enum MixMessage {
|
|||
|
||||
pub struct RandomEjectionMix {
|
||||
heartbeat_id: Tag<24>,
|
||||
last_heartbeat: DateTime<Local>,
|
||||
}
|
||||
|
||||
impl RandomEjectionMix {
|
||||
pub fn init(tag: Tag<24>) -> RandomEjectionMix {
|
||||
RandomEjectionMix { heartbeat_id: tag }
|
||||
RandomEjectionMix {
|
||||
heartbeat_id: tag,
|
||||
last_heartbeat: Local::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, tag: &Tag<24>, plaintext: &String) -> Option<MixMessage> {
|
||||
|
@ -42,15 +46,33 @@ impl RandomEjectionMix {
|
|||
}
|
||||
}
|
||||
|
||||
fn process_heartbeat(&self, tag: &Tag<24>, heartbeat: &DateTime<Local>) -> Option<MixMessage> {
|
||||
fn process_heartbeat(
|
||||
&mut self,
|
||||
tag: &Tag<24>,
|
||||
heartbeat: &DateTime<Local>,
|
||||
) -> Option<MixMessage> {
|
||||
if tag == &self.heartbeat_id {
|
||||
println!("Received HeartBeat @ {}", heartbeat);
|
||||
let new_heartbeat = Heartbeat(self.heartbeat_id.clone(), Local::now());
|
||||
println!("[DEBUG] Received HeartBeat from {}", heartbeat);
|
||||
self.last_heartbeat = heartbeat.clone();
|
||||
let now = Local::now();
|
||||
let new_heartbeat = Heartbeat(self.heartbeat_id.clone(), now.clone());
|
||||
return Some(new_heartbeat);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_heartbeat(&self) -> bool {
|
||||
let time_since_last = Local::now() - self.last_heartbeat;
|
||||
println!(
|
||||
"[DEBUG] Time since last heartbeat: {}s",
|
||||
time_since_last.num_seconds()
|
||||
);
|
||||
if time_since_last > Duration::minutes(2) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Actually do the Random Ejection Mixing...
|
||||
fn random_ejection_mix(&mut self, ciphertext: &TaggedCiphertext) -> TaggedCiphertext {
|
||||
ciphertext.clone()
|
||||
|
|
|
@ -3,6 +3,7 @@ use clap::Clap;
|
|||
use niwl::Profile;
|
||||
use niwl_rem::MixMessage::Heartbeat;
|
||||
use niwl_rem::{MixMessage, RandomEjectionMix};
|
||||
use rand::Rng;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Clap)]
|
||||
|
@ -52,6 +53,7 @@ fn main() {
|
|||
}
|
||||
SubCommand::Run(_cmd) => {
|
||||
let mut profile = Profile::get_profile(&opts.profile_filename);
|
||||
let filename = opts.profile_filename.clone();
|
||||
let server = opts.niwl_server.clone();
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
|
@ -60,7 +62,7 @@ fn main() {
|
|||
.block_on(async {
|
||||
let random_tag = profile.root_secret.tagging_key().generate_tag();
|
||||
let mut rem = RandomEjectionMix::init(random_tag.clone());
|
||||
println!("kicking off initial heartbeat...");
|
||||
println!("[DEBUG] kicking off initial heartbeat...");
|
||||
profile
|
||||
.send_to_self(
|
||||
&server,
|
||||
|
@ -68,15 +70,22 @@ fn main() {
|
|||
.unwrap(),
|
||||
)
|
||||
.await;
|
||||
println!("starting..");
|
||||
println!("[DEBUG] starting mixing loop");
|
||||
let detection_key = profile.root_secret.extract_detection_key(24);
|
||||
|
||||
loop {
|
||||
|
||||
|
||||
if rem.check_heartbeat() == false {
|
||||
println!("[ERROR] Niwl Server is Delaying Messages for more than 2 Minutes...Possible Attack...")
|
||||
}
|
||||
|
||||
match profile.detect_tags(&server).await {
|
||||
Ok(detected_tags) => {
|
||||
let mut latest_tag = None;
|
||||
for (tag, ciphertext) in detected_tags.detected_tags.iter() {
|
||||
if detection_key.test_tag(&tag) {
|
||||
random_delay().await;
|
||||
let plaintext = profile.private_key.decrypt(ciphertext);
|
||||
match plaintext {
|
||||
Some(plaintext) => match rem.push(tag, &plaintext) {
|
||||
|
@ -114,10 +123,10 @@ fn main() {
|
|||
}
|
||||
latest_tag = Some(tag.clone());
|
||||
}
|
||||
println!("Updating...");
|
||||
match &latest_tag {
|
||||
Some(tag) => {
|
||||
profile.update_previously_seen_tag(tag);
|
||||
profile.save(&filename);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -126,10 +135,18 @@ fn main() {
|
|||
println!("Error: {}", err)
|
||||
}
|
||||
}
|
||||
println!("sleeping..");
|
||||
tokio::time::sleep(Duration::new(5, 0)).await;
|
||||
|
||||
random_delay().await;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn random_delay() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let seconds = rng.gen_range(0..10);
|
||||
let nanos = rng.gen_range(0..1_000_000_000);
|
||||
println!("[DEBUG] Waiting {}.{}s", seconds, nanos);
|
||||
tokio::time::sleep(Duration::new(seconds, nanos)).await;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue