forked from openprivacy/niwl
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
|
||||
|
||||
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.
|
|
@ -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)
|
||||
|
|
|
@ -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> =
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
Loading…
Reference in New Issue