Message padding + Heartbeat mixing
This commit is contained in:
parent
b333e4f031
commit
89fbfb91d4
27
README.md
27
README.md
|
@ -60,7 +60,7 @@ posted to the Niwl Server. The new decrypted message takes its place in the mess
|
||||||
### On the Privacy of REMs
|
### On the Privacy of REMs
|
||||||
|
|
||||||
Fuzzytags themselves can only be linked to receivers via those in position of a RootSecret *or* Niwl Servers who
|
Fuzzytags themselves can only be linked to receivers via those in position of a RootSecret *or* Niwl Servers who
|
||||||
possess the `VerificationKey` - as such, assuming that there is no collusion between a particular REM and a Niwl Server
|
possess the `DetectionKey` - as such, assuming that there is no collusion between a particular REM and a Niwl Server
|
||||||
there is no mechanism through which a REM can associate message with a (set of) receiver(s).
|
there is no mechanism through which a REM can associate message with a (set of) receiver(s).
|
||||||
|
|
||||||
Further, (again assuming no collusion between a particular REM and a Niwl Server), there is no mechanism for a REM to associate
|
Further, (again assuming no collusion between a particular REM and a Niwl Server), there is no mechanism for a REM to associate
|
||||||
|
@ -118,6 +118,21 @@ of the day (or week...etc.) - the only practical defense to this is to have more
|
||||||
niwl system other than mixers - as traffic diversity increases, the less utility tells like frequency of message
|
niwl system other than mixers - as traffic diversity increases, the less utility tells like frequency of message
|
||||||
sends ultimately have.
|
sends ultimately have.
|
||||||
|
|
||||||
|
|
||||||
|
### Encryption
|
||||||
|
|
||||||
|
For the purposes of this prototype message are encrypted using a simple one-use, unidirectional diffie-hellman derived key,
|
||||||
|
where the sending party generates an ephemeral keypair (which then uses libsodiums secretbox to perform the actual encryption).
|
||||||
|
This key binds the message to a particular fuzzytag (which prevents tampering) but does nothing else to certify the
|
||||||
|
authenticity of the message.
|
||||||
|
|
||||||
|
Because of this only confidentiality and integrity of the message contents is asserted - no authentication mechanisms is provided.
|
||||||
|
Any party that knows the `PublicKey` and the public `TaggingKey` of another party can encrypt and send messages to them,
|
||||||
|
and the recipient party has no mechanism to certify the origin of these messages.
|
||||||
|
|
||||||
|
Any applications built on top of Niwl need to provide an additional encryption layer that provides authenticity
|
||||||
|
(e.g. a complete diffie-hellman key exchange involving pre=exchanged long term identity public keys).
|
||||||
|
|
||||||
### Notes on IP and other networking Metadata.
|
### Notes on IP and other networking Metadata.
|
||||||
|
|
||||||
niwl is designed to provide metadata security when operated over an unprotected network. Ideally, a niwl server should
|
niwl is designed to provide metadata security when operated over an unprotected network. Ideally, a niwl server should
|
||||||
|
@ -128,6 +143,12 @@ Clients may wish to hide their use of niwl from a network adversary (at a risk o
|
||||||
This will also further reduce the ability of niwl to correlate senders with specific behaviour and can be seen as
|
This will also further reduce the ability of niwl to correlate senders with specific behaviour and can be seen as
|
||||||
complimentary, but optional.
|
complimentary, but optional.
|
||||||
|
|
||||||
|
### Notes on future work and expansions
|
||||||
|
|
||||||
|
There is no reason that a client could chain a sequence of mixers together via onion encrypted their original
|
||||||
|
message to multiple mix nodes. In that sense we can treat the system as a superposition of free-route mix networks.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Code Overview
|
# Code Overview
|
||||||
|
|
||||||
|
@ -186,4 +207,6 @@ For a more detailed overview please check out each individual crate.
|
||||||
|
|
||||||
## References
|
## 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.
|
* 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.
|
||||||
|
|
||||||
|
* Sampigethaya, Krishna, and Radha Poovendran. "A survey on mix networks and their secure applications." Proceedings of the IEEE 94.12 (2006): 2142-2181.
|
|
@ -101,7 +101,7 @@ fn main() {
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.block_on(async {
|
.block_on(async {
|
||||||
let result = profile.tag_and_send(server, contact, &cmd.message).await;
|
let result = profile.tag_and_send(&server, contact, &cmd.message).await;
|
||||||
println!("{}", result.unwrap().text().await.unwrap());
|
println!("{}", result.unwrap().text().await.unwrap());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -129,17 +129,29 @@ fn main() {
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.block_on(async {
|
.block_on(async {
|
||||||
match profile.detect_tags(server).await {
|
match profile.detect_tags(&server).await {
|
||||||
Ok(detected_tags) => {
|
Ok(detected_tags) => {
|
||||||
|
let mut count = 0;
|
||||||
|
let mut to_me_count = 0;
|
||||||
for (tag, ciphertext) in detected_tags.detected_tags.iter() {
|
for (tag, ciphertext) in detected_tags.detected_tags.iter() {
|
||||||
|
count += 1;
|
||||||
match profile.private_key.decrypt(ciphertext) {
|
match profile.private_key.decrypt(ciphertext) {
|
||||||
Some(message) => {
|
Some(message) => {
|
||||||
|
to_me_count += 1;
|
||||||
println!("message: {}", message)
|
println!("message: {}", message)
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
profile.update_previously_seen_tag(tag);
|
profile.update_previously_seen_tag(tag);
|
||||||
}
|
}
|
||||||
|
if count > 0 {
|
||||||
|
println!(
|
||||||
|
"Received {} Messages from server. {} were true positives.",
|
||||||
|
count, to_me_count
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("Received no messages.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("Error: {}", err)
|
println!("Error: {}", err)
|
||||||
|
|
|
@ -24,12 +24,7 @@ impl RandomEjectionMix {
|
||||||
pub fn init(tag: Tag<24>) -> RandomEjectionMix {
|
pub fn init(tag: Tag<24>) -> RandomEjectionMix {
|
||||||
let mut store = vec![];
|
let mut store = vec![];
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let random_tag = RootSecret::<24>::generate().tagging_key().generate_tag();
|
store.push(RandomEjectionMix::get_random());
|
||||||
let random_secret = PrivateKey::generate();
|
|
||||||
let random_encryption = random_secret
|
|
||||||
.public_key()
|
|
||||||
.encrypt(&random_tag, &String::new());
|
|
||||||
store.push(random_encryption);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RandomEjectionMix {
|
RandomEjectionMix {
|
||||||
|
@ -39,6 +34,15 @@ impl RandomEjectionMix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_random() -> TaggedCiphertext {
|
||||||
|
let random_tag = RootSecret::<24>::generate().tagging_key().generate_tag();
|
||||||
|
let random_secret = PrivateKey::generate();
|
||||||
|
let random_encryption = random_secret
|
||||||
|
.public_key()
|
||||||
|
.encrypt(&random_tag, &String::new());
|
||||||
|
random_encryption
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, tag: &Tag<24>, plaintext: &String) -> Option<MixMessage> {
|
pub fn push(&mut self, tag: &Tag<24>, plaintext: &String) -> Option<MixMessage> {
|
||||||
// The plaintext can either be a TaggedCiphertext OR a HeartBeat
|
// The plaintext can either be a TaggedCiphertext OR a HeartBeat
|
||||||
let message: serde_json::Result<TaggedCiphertext> =
|
let message: serde_json::Result<TaggedCiphertext> =
|
||||||
|
|
|
@ -3,7 +3,7 @@ use clap::Clap;
|
||||||
use niwl::Profile;
|
use niwl::Profile;
|
||||||
use niwl_rem::MixMessage::Heartbeat;
|
use niwl_rem::MixMessage::Heartbeat;
|
||||||
use niwl_rem::{MixMessage, RandomEjectionMix};
|
use niwl_rem::{MixMessage, RandomEjectionMix};
|
||||||
use rand::Rng;
|
use rand::{thread_rng, Rng};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Clap)]
|
#[derive(Clap)]
|
||||||
|
@ -77,9 +77,26 @@ fn main() {
|
||||||
|
|
||||||
|
|
||||||
if rem.check_heartbeat() == false {
|
if rem.check_heartbeat() == false {
|
||||||
println!("[ERROR] Niwl Server is Delaying Messages for more than 2 Minutes...Possible Attack...")
|
println!("[ERROR] Niwl Server is Delaying Messages for more than 2 Minutes...Possible Attack...");
|
||||||
|
let num_messages : i32 = thread_rng().gen_range(0..100);
|
||||||
|
// Kick out a random number of messages...
|
||||||
|
for i in 0..num_messages {
|
||||||
|
random_delay();
|
||||||
|
profile.send_to_self(&server, &serde_json::to_string(
|
||||||
|
&RandomEjectionMix::get_random(),
|
||||||
|
).unwrap()).await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// After every heart beat kick out a random
|
||||||
|
// message so we wil eventually clear the pool
|
||||||
|
random_delay();
|
||||||
|
profile.send_to_self(&server,&serde_json::to_string(
|
||||||
|
&RandomEjectionMix::get_random(),
|
||||||
|
).unwrap()).await;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
match profile.detect_tags(&server).await {
|
match profile.detect_tags(&server).await {
|
||||||
Ok(detected_tags) => {
|
Ok(detected_tags) => {
|
||||||
let mut latest_tag = None;
|
let mut latest_tag = None;
|
||||||
|
|
|
@ -29,6 +29,13 @@ pub struct PublicKey(RistrettoPoint);
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
/// Encrypt to Tag provides uni-directional encrypted
|
/// Encrypt to Tag provides uni-directional encrypted
|
||||||
pub fn encrypt(&self, tag: &Tag<24>, message: &String) -> TaggedCiphertext {
|
pub fn encrypt(&self, tag: &Tag<24>, message: &String) -> TaggedCiphertext {
|
||||||
|
let mut paddedMessage = message.clone();
|
||||||
|
if message.len() < 1024 {
|
||||||
|
for _i in message.len()..1024 {
|
||||||
|
paddedMessage += " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a random point. We will use the public part as a nonce
|
// Generate a random point. We will use the public part as a nonce
|
||||||
// And the private part to generate a key.
|
// And the private part to generate a key.
|
||||||
let mut rng = OsRng::default();
|
let mut rng = OsRng::default();
|
||||||
|
@ -51,7 +58,7 @@ impl PublicKey {
|
||||||
let secret_box = SecretBox::new(key, Salsa20).unwrap();
|
let secret_box = SecretBox::new(key, Salsa20).unwrap();
|
||||||
|
|
||||||
// TODO: Fixed Size Packets
|
// TODO: Fixed Size Packets
|
||||||
let ciphertext = secret_box.seal(message.as_bytes(), nonce);
|
let ciphertext = secret_box.seal(paddedMessage.as_bytes(), nonce);
|
||||||
TaggedCiphertext {
|
TaggedCiphertext {
|
||||||
tag: tag.clone(),
|
tag: tag.clone(),
|
||||||
nonce: z,
|
nonce: z,
|
||||||
|
@ -91,7 +98,7 @@ impl PrivateKey {
|
||||||
let secret_box = SecretBox::new(key, Salsa20).unwrap();
|
let secret_box = SecretBox::new(key, Salsa20).unwrap();
|
||||||
match secret_box.unseal(ciphertext.ciphertext.as_slice(), nonce) {
|
match secret_box.unseal(ciphertext.ciphertext.as_slice(), nonce) {
|
||||||
Some(plaintext) => match String::from_utf8(plaintext) {
|
Some(plaintext) => match String::from_utf8(plaintext) {
|
||||||
Ok(plaintext) => Some(plaintext),
|
Ok(plaintext) => Some(String::from(plaintext.trim_end())),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue