niwl/niwl/src/lib.rs

238 lines
7.5 KiB
Rust

#![feature(into_future)]
use crate::encrypt::{PrivateKey, PublicKey, TaggedCiphertext};
use fuzzytags::{DetectionKey, RootSecret, Tag, TaggingKey};
use reqwest::{Error, Response};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::Write;
pub mod encrypt;
#[derive(Debug)]
pub enum NiwlError {
NoKnownContactError(String),
RemoteServerError(String),
}
#[derive(Serialize, Deserialize)]
pub struct Profile {
profile_name: String,
pub root_secret: RootSecret<24>,
pub private_key: PrivateKey,
tagging_keys: HashMap<String, (TaggingKey<24>, PublicKey)>,
detection_key_length: usize,
last_seen_tag: Option<Tag<24>>,
}
#[derive(Serialize, Deserialize)]
pub struct KeySet {
profile_name: String,
tagging_key: TaggingKey<24>,
public_key: PublicKey,
}
#[derive(Deserialize)]
pub struct DetectedTags {
pub detected_tags: Vec<(Tag<24>, TaggedCiphertext)>,
}
#[derive(Serialize, Deserialize)]
pub struct FetchMessagesRequest {
// The last tag this client downloaded to use as a reference when fetching new messages
// If None, then the server will check *all* messages.
pub reference_tag: Option<Tag<24>>,
// The detection key to use to fetch new messages
pub detection_key: DetectionKey<24>,
}
#[derive(Serialize, Deserialize)]
pub struct PostMessageRequest {
pub tag: Tag<24>,
pub ciphertext: TaggedCiphertext,
}
impl Profile {
pub fn get_profile(profile_filename: &String) -> Profile {
match fs::read_to_string(profile_filename) {
Ok(json) => serde_json::from_str(json.as_str()).unwrap(),
Err(why) => {
panic!("couldn't read orb.profile : {}", why);
}
}
}
pub fn new(profile_name: String, detection_key_length: usize) -> Profile {
let root_secret = RootSecret::<24>::generate();
let private_key = PrivateKey::generate();
Profile {
profile_name,
root_secret,
private_key,
tagging_keys: Default::default(),
detection_key_length,
last_seen_tag: None,
}
}
pub fn keyset(&self) -> KeySet {
let tagging_key = self.root_secret.tagging_key();
let public_key = self.private_key.public_key();
KeySet {
profile_name: self.profile_name.clone(),
tagging_key,
public_key,
}
}
pub fn save(&self, profile_filename: &String) -> std::io::Result<()> {
let j = serde_json::to_string(&self);
let mut file = match File::create(profile_filename) {
Err(why) => panic!("couldn't create : {}", why),
Ok(file) => file,
};
file.write_all(j.unwrap().as_bytes())
}
pub fn generate_tag(&self, id: &String) -> Result<Tag<24>, NiwlError> {
if self.tagging_keys.contains_key(id) {
let tag = self.tagging_keys[id].0.generate_tag();
println!("Tag for {} {}", id, tag.to_string());
return Ok(tag);
}
Err(NiwlError::NoKnownContactError(format!(
"No known friend {}. Perhaps you need to import-tagging-key first?",
id
)))
}
pub fn import_tagging_key(&mut self, key: &String) {
match base32::decode(base32::Alphabet::RFC4648 { padding: false }, key.as_str()) {
Some(data) => {
let tagging_key_result: Result<KeySet, bincode::Error> =
bincode::deserialize(&data);
match tagging_key_result {
Ok(hotk) => {
println!("Got: {}: {}", hotk.profile_name, hotk.tagging_key.id());
if self.tagging_keys.contains_key(&hotk.profile_name) == false {
self.tagging_keys
.insert(hotk.profile_name, (hotk.tagging_key, hotk.public_key));
} else {
println!("There is already an entry for {}", hotk.profile_name)
}
return;
}
Err(err) => {
println!("Error: {}", err.to_string());
}
}
}
_ => {}
};
println!("Error Reporting Tagging Key")
}
pub async fn tag_and_mix(
&self,
server: String,
mix: String,
contact: String,
message: &String,
) -> Result<Response, NiwlError> {
match self.generate_tag(&contact) {
Ok(tag) => {
let ciphertext = self.tagging_keys[&contact].1.encrypt(&tag, message);
let ciphertext_json = serde_json::to_string(&ciphertext).unwrap();
return self.tag_and_send(&server, mix, &ciphertext_json).await;
}
Err(err) => Err(err),
}
}
pub async fn send_to_self(
&self,
server: &String,
message: &String,
) -> Result<Response, NiwlError> {
let client = reqwest::Client::new();
let tag = self.root_secret.tagging_key().generate_tag();
let ciphertext = self.private_key.public_key().encrypt(&tag, message);
let result = client
.post(&format!("{}/new", server))
.json(&PostMessageRequest { tag, ciphertext })
.send()
.await;
match result {
Ok(response) => Ok(response),
Err(err) => Err(NiwlError::RemoteServerError(err.to_string())),
}
}
pub async fn forward(
&self,
server: &String,
message: &TaggedCiphertext,
) -> Result<Response, NiwlError> {
let client = reqwest::Client::new();
let tag = message.tag.clone();
let ciphertext = message.clone();
let result = client
.post(&format!("{}/new", server))
.json(&PostMessageRequest { tag, ciphertext })
.send()
.await;
match result {
Ok(response) => Ok(response),
Err(err) => Err(NiwlError::RemoteServerError(err.to_string())),
}
}
pub async fn tag_and_send(
&self,
server: &String,
contact: String,
message: &String,
) -> Result<Response, NiwlError> {
let client = reqwest::Client::new();
match self.generate_tag(&contact) {
Ok(tag) => {
let ciphertext = self.tagging_keys[&contact].1.encrypt(&tag, message);
let result = client
.post(&format!("{}/new", server))
.json(&PostMessageRequest { tag, ciphertext })
.send()
.await;
match result {
Ok(response) => Ok(response),
Err(err) => Err(NiwlError::RemoteServerError(err.to_string())),
}
}
Err(err) => Err(err),
}
}
pub async fn detect_tags(&mut self, server: &String) -> Result<DetectedTags, Error> {
let client = reqwest::Client::new();
let detection_key = self
.root_secret
.extract_detection_key(self.detection_key_length);
let result = client
.post(&format!("{}/tags", server))
.json(&FetchMessagesRequest {
reference_tag: self.last_seen_tag.clone(),
detection_key,
})
.send()
.await;
result.unwrap().json().await
}
pub fn update_previously_seen_tag(&mut self, tag: &Tag<24>) {
self.last_seen_tag = Some(tag.clone());
}
}