Message padding + Heartbeat mixing

This commit is contained in:
Sarah Jamie Lewis 2021-05-18 15:13:44 -07:00
parent b333e4f031
commit 89fbfb91d4
6 changed files with 77 additions and 14 deletions

View File

@ -60,7 +60,7 @@ posted to the Niwl Server. The new decrypted message takes its place in the mess
### On the Privacy of REMs
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).
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
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.
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
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
@ -186,4 +207,6 @@ For a more detailed overview please check out each individual crate.
## 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.

View File

@ -101,7 +101,7 @@ fn main() {
.build()
.unwrap()
.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());
});
}
@ -129,17 +129,29 @@ fn main() {
.build()
.unwrap()
.block_on(async {
match profile.detect_tags(server).await {
match profile.detect_tags(&server).await {
Ok(detected_tags) => {
let mut count = 0;
let mut to_me_count = 0;
for (tag, ciphertext) in detected_tags.detected_tags.iter() {
count += 1;
match profile.private_key.decrypt(ciphertext) {
Some(message) => {
to_me_count += 1;
println!("message: {}", message)
}
_ => {}
}
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) => {
println!("Error: {}", err)

View File

@ -24,12 +24,7 @@ impl RandomEjectionMix {
pub fn init(tag: Tag<24>) -> RandomEjectionMix {
let mut store = vec![];
for i in 0..10 {
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());
store.push(random_encryption);
store.push(RandomEjectionMix::get_random());
}
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> {
// The plaintext can either be a TaggedCiphertext OR a HeartBeat
let message: serde_json::Result<TaggedCiphertext> =

View File

@ -3,7 +3,7 @@ use clap::Clap;
use niwl::Profile;
use niwl_rem::MixMessage::Heartbeat;
use niwl_rem::{MixMessage, RandomEjectionMix};
use rand::Rng;
use rand::{thread_rng, Rng};
use std::time::Duration;
#[derive(Clap)]
@ -77,9 +77,26 @@ fn main() {
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 {
Ok(detected_tags) => {
let mut latest_tag = None;

View File

@ -29,6 +29,13 @@ pub struct PublicKey(RistrettoPoint);
impl PublicKey {
/// Encrypt to Tag provides uni-directional encrypted
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
// And the private part to generate a key.
let mut rng = OsRng::default();
@ -51,7 +58,7 @@ impl PublicKey {
let secret_box = SecretBox::new(key, Salsa20).unwrap();
// TODO: Fixed Size Packets
let ciphertext = secret_box.seal(message.as_bytes(), nonce);
let ciphertext = secret_box.seal(paddedMessage.as_bytes(), nonce);
TaggedCiphertext {
tag: tag.clone(),
nonce: z,
@ -91,7 +98,7 @@ impl PrivateKey {
let secret_box = SecretBox::new(key, Salsa20).unwrap();
match secret_box.unseal(ciphertext.ciphertext.as_slice(), nonce) {
Some(plaintext) => match String::from_utf8(plaintext) {
Ok(plaintext) => Some(plaintext),
Ok(plaintext) => Some(String::from(plaintext.trim_end())),
Err(_) => None,
},
None => None,

Binary file not shown.