Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions reverse-proxy/src/handler/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Mutex, MutexGuard};
use log::debug;
use ntor::common::{InitSessionMessage, NTorParty};
Expand Down Expand Up @@ -126,10 +127,16 @@ impl ReverseHandler {
Err(res) => return res,
};

let compression = ctx
.get_request_header()
.get("x-compression")
.map(|val| utils::compression::CompressorVariant::from_str(val).unwrap());

let wrapped_request = match ProxyHandler::decrypt_request_body(
request_body,
self.config.ntor_server_id.clone(),
shared_secret.clone(),
compression,
) {
Ok(req) => req,
Err(res) => return res,
Expand Down
50 changes: 34 additions & 16 deletions reverse-proxy/src/handler/proxy/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,50 @@ impl ProxyHandler {
request_body: EncryptedMessage,
ntor_server_id: String,
shared_secret: Vec<u8>,
) -> Result<L8RequestObject, APIHandlerResponse>
{
compression: Option<utils::compression::CompressorVariant>,
) -> Result<L8RequestObject, APIHandlerResponse> {
let mut ntor_server = NTorServer::new(ntor_server_id);
ntor_server.set_shared_secret(shared_secret.clone());

// Decrypt the request body using nTor shared secret
let decrypted_data = ntor_server.decrypt(ntor::common::EncryptedMessage {
nonce: <[u8; 12]>::try_from(request_body.nonce).unwrap(),
data: request_body.data,
}).map_err(|err| {
return APIHandlerResponse {
status: StatusCode::BAD_REQUEST,
body: Some(format!("Decryption failed: {}", err).as_bytes().to_vec()),
};
})?;
// let decrypted_data = request_body.data;

// parse decrypted data into WrappedUserRequest
let wrapped_request: L8RequestObject = bytes_to_json(decrypted_data)
let mut decrypted_data = ntor_server
.decrypt(ntor::common::EncryptedMessage {
nonce: <[u8; 12]>::try_from(request_body.nonce).unwrap(),
data: request_body.data,
})
.map_err(|err| {
return APIHandlerResponse {
status: StatusCode::BAD_REQUEST,
body: Some(format!("Failed to parse request body: {}", err).as_bytes().to_vec()),
body: Some(format!("Decryption failed: {}", err).as_bytes().to_vec()),
};
})?;

// decompress the data if compression is specified
if let Some(variant) = compression {
log::warn!(
"Size of decrypted data before decompression: {}",
decrypted_data.len()
);
decrypted_data = utils::compression::decompress_data(&variant, &decrypted_data);
log::warn!(
"Size of decrypted data after decompression: {}",
decrypted_data.len()
);
}

// parse decrypted data into WrappedUserRequest
let wrapped_request: L8RequestObject = bytes_to_json(decrypted_data).map_err(|err| {
log::error!("Failed to parse request body: {}", err);
return APIHandlerResponse {
status: StatusCode::BAD_REQUEST,
body: Some(
format!("Failed to parse request body: {}", err)
.as_bytes()
.to_vec(),
),
};
})?;

Ok(wrapped_request)
}

Expand Down
7 changes: 5 additions & 2 deletions spa/frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { initEncryptedTunnel, ServiceProvider } from "interceptor-wasm"
import { initEncryptedTunnel, ServiceProvider, ServiceProviderOptions } from "interceptor-wasm"

let forward_proxy_url = import.meta.env.VITE_FORWARD_PROXY_URL || 'http://localhost:6191';
let backend_url = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3000';

const layer8_ = async () => {
try {
let providers = [ServiceProvider.new(backend_url)];
let options = new ServiceProviderOptions();
options.compression = "gzip";

let providers = [ServiceProvider.new(backend_url, options)];
await initEncryptedTunnel(forward_proxy_url, providers).finally(() => {
console.log('Encrypted tunnel initialized successfully');
});
Expand Down
1 change: 1 addition & 0 deletions utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ serde_json = "1.0.140"
serde = { version = "1.0.219", features = ["derive"] }
base64 = "0.21.7"
log = "0.4.27"
flate2 = "1.1.2"
116 changes: 116 additions & 0 deletions utils/src/compression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use std::io::prelude::*;
use std::str::FromStr;

use flate2::Compression;
use flate2::write::{GzEncoder, ZlibEncoder};

#[derive(Debug)]
pub enum CompressorVariant {
Zlib,
Gzip,
// Deflate, // To be used when experimenting with with chunked data compression: <https://stackoverflow.com/a/10168441/10020745>
}

impl CompressorVariant {
pub fn as_str(&self) -> &str {
match self {
CompressorVariant::Zlib => "zlib",
CompressorVariant::Gzip => "gzip",
}
}
}

impl Default for CompressorVariant {
fn default() -> Self {
CompressorVariant::Zlib
}
}

impl FromStr for CompressorVariant {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"zlib" => Ok(CompressorVariant::Zlib),
"gzip" => Ok(CompressorVariant::Gzip),
_ => Ok(CompressorVariant::default()),
}
}
}

pub fn compress_data(variant: &CompressorVariant, data: &[u8]) -> Vec<u8> {
match variant {
CompressorVariant::Zlib => {
// Compress using Zlib
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
encoder
.write_all(data)
.expect("Failed to write data to Zlib encoder");
encoder.finish().expect("Failed to finish Zlib encoding")
}
CompressorVariant::Gzip => {
// Compress using Gzip
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder
.write_all(data)
.expect("Failed to write data to Gzip encoder");
encoder.finish().expect("Failed to finish Gzip encoding")
}
}
}

pub fn decompress_data(variant: &CompressorVariant, data: &[u8]) -> Vec<u8> {
match variant {
CompressorVariant::Zlib => {
// Decompress using Zlib
let mut decoder = flate2::read::ZlibDecoder::new(data);
let mut decoded_data = Vec::new();
decoder
.read_to_end(&mut decoded_data)
.expect("Failed to read data from Zlib decoder");
decoded_data
}
CompressorVariant::Gzip => {
// Decompress using Gzip
let mut decoder = flate2::read::GzDecoder::new(data);
let mut decoded_data = Vec::new();
decoder
.read_to_end(&mut decoded_data)
.expect("Failed to read data from Gzip decoder");
decoded_data
}
}
}

#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_zlib_compression() {
let data = b"Hello, world!";
let compressed = compress_data(&CompressorVariant::Zlib, data);
let decompressed = decompress_data(&CompressorVariant::Zlib, &compressed);
assert_eq!(data.to_vec(), decompressed);
}

#[test]
fn test_gzip_compression() {
let data = b"Hello, world!";
let compressed = compress_data(&CompressorVariant::Gzip, data);
let decompressed = decompress_data(&CompressorVariant::Gzip, &compressed);
assert_eq!(data.to_vec(), decompressed);
}

#[test]
fn test_compression_consistency() {
let data = b"Hello, world! This is a test of the compression and decompression functions.";
let compressed_zlib = compress_data(&CompressorVariant::Zlib, data);
let decompressed_zlib = decompress_data(&CompressorVariant::Zlib, &compressed_zlib);
assert_eq!(data.to_vec(), decompressed_zlib);
let compressed_gzip = compress_data(&CompressorVariant::Gzip, data);
let decompressed_gzip = decompress_data(&CompressorVariant::Gzip, &compressed_gzip);
assert_eq!(data.to_vec(), decompressed_gzip);
}
}
2 changes: 2 additions & 0 deletions utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use serde::{Deserialize, Serialize};
use log::error;

pub mod compression;

pub fn to_reqwest_header(map: HashMap<String, String>) -> HeaderMap {
let mut header_map = HeaderMap::new();
for (k, v) in map {
Expand Down
Loading