Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim-Leon committed Mar 30, 2024
1 parent fa19359 commit e577af8
Show file tree
Hide file tree
Showing 18 changed files with 940 additions and 163 deletions.
719 changes: 716 additions & 3 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@ edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["web"]
s3 = []
proto = []
wasm = []
# Stream API compatibility, mpeg-dash video stream support
stream = []
# Listen changes in the Storage, synchronization API support. for listening and parsing webhook events.
sync = []
# Compress files before transit.
compression = []
#

web = ["wasm", "proto"]
cli = ["proto", "s3", "native"]
native = [] #"dep:tokio", "dep:tokio-stream"

[dependencies]
bucket-common-types = { git = "https://github.com/Tim-Leon/bucket-common-types.git", features = [
"secret_share_link",
Expand Down Expand Up @@ -60,6 +70,8 @@ pkg-version = "1.0.0"
log = "0.4.20"
prokio = "0.1.0"
email_address = "0.2.4"
mnemonic = "1.1.1"
dash-mpd = {version = "0.15.0", features = ["tokio"],optional = true}

[target.'cfg(unix)'.dependencies]
fuser = "0.14.0" # https://crates.io/crates/fuser
Expand Down
7 changes: 5 additions & 2 deletions src/controller/account/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::client::query_client::backend_api::{AccountLoginFinishRequest, Accoun
use crate::client::query_client::QueryClient;
use crate::controller::account::errors::{LoginError, RegisterError};

use crate::encryption_v1::encryption::create_ed25519_signing_keys;
use crate::encryption_v1::hash::password_strength;
use crate::{
constants::PASSWORD_STRENGTH_SCORE,
Expand Down Expand Up @@ -86,7 +87,7 @@ pub async fn register(
if password_strength(&email, &password, None)? < PASSWORD_STRENGTH_SCORE {
return Err(RegisterError::PasswordTooWeak);
}
let secrets = encryption_v1::encryption::setup(password, email)?;
let master_key = encryption_v1::encryption::setup(password, email)?;
let mut rng = rand::thread_rng();
let oprf_start =
opaque_ke::ClientRegistration::<DefaultCipherSuite>::start(&mut rng, password.as_bytes())
Expand All @@ -109,11 +110,13 @@ pub async fn register(
ClientRegistrationFinishParameters::default(),
)?;

let signing_key = create_ed25519_signing_keys(&master_key).unwrap();

let finish_req = CreateAccountFinishRequest {
oprf: oprf_finish.message.serialize().to_vec(),
username: username.to_string(),
session_id: start_resp.session_id,
public_signing_key: secrets.get_ed25519_public_signing_key().as_slice().to_vec(),
public_signing_key: signing_key.pk.to_vec(),
};
let finish_resp = query_client
.create_account_finish(finish_req)
Expand Down
6 changes: 3 additions & 3 deletions src/controller/bucket/download_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ impl BucketFileDownloadHandler for WebBucketFileWriter {
match decrypted_buffer {
Either::Left(decrypted_buffer) => {
self.write_target_file
.write_chunk(&decrypted_buffer, self.offset);
.write_chunk(&decrypted_buffer, self.offset).unwrap();
self.offset += decrypted_buffer.len() as u64;
}
Either::Right(decrypted_buffer) => {
self.write_target_file
.write_chunk(&decrypted_buffer, self.offset);
.write_chunk(&decrypted_buffer, self.offset).unwrap();
self.offset += decrypted_buffer.len() as u64;
}
}
Expand All @@ -120,7 +120,7 @@ impl BucketFileDownloadHandler for WebBucketFileWriter {
match self.decryption_module {
None => {}
Some(module) => {
module.finalize()?; //TODO: Invalid Signature
module.finalize(); //TODO: Invalid Signature
}
}

Expand Down
17 changes: 9 additions & 8 deletions src/controller/bucket/upload_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub trait BucketFileUploadHandler {
// Called when a chunk is uploaded. returns the chunk to be uploaded. It's up to the implementation to encrypt the chunk if the bucket is encrypted.
async fn on_upload_chunk(&mut self, chunk_size: u64) -> Result<Vec<u8>, Self::Error>;
// Called when the last chunk has been uploaded. In this method the user is still able to upload data, if so it will return a Vec.
fn on_upload_finish(self) -> Result<Option<Vec<u8>>, Self::Error>;
fn on_upload_finish(self) -> Result<(), Self::Error>;
}

#[async_trait(?Send)]
Expand All @@ -56,7 +56,8 @@ impl BucketFileUploadHandler for BucketFileReader {
) -> Result<u64, Self::Error> {
match bucket_encryption {
Some(_bucket_encryption) => match &self.encryption_module {
Some(_encryption_module) => {}
Some(_encryption_module) => {
}
None => {
return Err(BucketDownloadHandlerFileErrors::EncryptionModuleNotInitialized);
}
Expand All @@ -76,18 +77,18 @@ impl BucketFileUploadHandler for BucketFileReader {

match &mut self.encryption_module {
Some(x) => {
let decrypted_bytes = x.update(bytes)?;
Ok(decrypted_bytes)
let encrypted_bytes = x.update(bytes)?;
Ok(encrypted_bytes)
}
None => Ok(bytes),
}
}

fn on_upload_finish(self) -> Result<Option<Vec<u8>>, Self::Error> {
let signed_hash: Option<Vec<u8>> = match self.encryption_module {
Some(x) => Some(x.finalize()?),
fn on_upload_finish(self) -> Result<(), Self::Error> {
let signed_hash = match self.encryption_module {
Some(x) => Some(x.finalize()),
None => return Err(Self::Error::EncryptionModuleNotInitialized),
};
Ok(signed_hash)
Ok(())
}
}
2 changes: 2 additions & 0 deletions src/encryption_v1/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ pub const HIGHWAY_HASH_KEY: [u64; 4] = [
];

pub const V1_ENCRYPTION_PASSWORD_SALT: &str = "";

pub const V1_X25519_SIGNATURE_HASH_SALT:&str = "";
39 changes: 12 additions & 27 deletions src/encryption_v1/decryption_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,28 @@ use highway::HighwayHash;
use crate::encryption_v1::constants::{
AES_GCM_NONCE, HIGHWAY_HASH_KEY, SHARE_LINK_SIGNATURE_NOISE,
};
use crate::encryption_v1::encryption::{generate_bucket_encryption_key, ClientSecrets};
use crate::encryption_v1::encryption::{generate_bucket_encryption_key, MasterKey};

use super::hash_based_signature::Ed25519HighwayHashBasedSignature;

pub trait DecryptionModule {
type Error;
/// Returns vector of decrypted data
fn update(&mut self, ciphertext: impl AsRef<[u8]>) -> Result<Vec<u8>, Self::Error>;
/// Finalize decryption by returning Vector which could contain hash/signature, get creative.
fn finalize(self) -> Result<(), Self::Error>;
fn finalize(self);
}
#[derive(Clone)]
pub struct ZeroKnowledgeDecryptionModuleV1 {
secrets: Arc<ClientSecrets>,
secrets: Arc<MasterKey>,
bucket_symmetric_encryption_key: aes_gcm::Aes256Gcm,
hasher: highway::HighwayHasher,
nonce: Nonce<typenum::U12>,
ed25519_noise: Noise,
signature: Option<Signature>,
hash_based_signature: Option<Ed25519HighwayHashBasedSignature>,
}

impl ZeroKnowledgeDecryptionModuleV1 {
pub fn new(
secrets: Arc<ClientSecrets>,
secrets: Arc<MasterKey>,
bucket_id: &uuid::Uuid,
signature: Option<Signature>,
) -> Self {
Self {
secrets: secrets.clone(),
Expand All @@ -41,10 +39,8 @@ impl ZeroKnowledgeDecryptionModuleV1 {
bucket_id,
)
.unwrap(),
hasher: highway::HighwayHasher::new(highway::Key(HIGHWAY_HASH_KEY)),
nonce: *Nonce::from_slice(&AES_GCM_NONCE), //TODO: NONCE should come from the bucket name?
ed25519_noise: Noise::from_slice(&SHARE_LINK_SIGNATURE_NOISE).unwrap(),
signature,
hash_based_signature: None
}
}
}
Expand All @@ -60,26 +56,15 @@ pub enum DecryptionError {
impl DecryptionModule for ZeroKnowledgeDecryptionModuleV1 {
type Error = DecryptionError;
fn update(&mut self, ciphertext: impl AsRef<[u8]>) -> Result<Vec<u8>, Self::Error> {
self.hasher.append(ciphertext.as_ref());
let plaintext = self
.bucket_symmetric_encryption_key
.decrypt(&self.nonce, ciphertext.as_ref())
.map_err(DecryptionError::FailedToDecryptChunk)?;
Ok(plaintext)
}

fn finalize(self) -> Result<(), Self::Error> {
let hash_result = self.hasher.finalize256();
match self.signature {
None => {}
Some(signature) => {
self.secrets
.ed25519_keypair
.pk
.verify(bytemuck::bytes_of(&hash_result), &signature)
.map_err(DecryptionError::InvalidSignature)?;
}
}
Ok(())

fn finalize(self) {

}

}
117 changes: 31 additions & 86 deletions src/encryption_v1/encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use aes_gcm::{
aead::{generic_array::typenum, Aead},
KeyInit, Nonce,
};
use argon2::password_hash::SaltString;

use std::sync::Arc;
//use sha2::Sha512;
Expand All @@ -13,8 +14,10 @@ use crate::encryption_v1::hash::{

use highway::HighwayHash;

use sha3::{digest::InvalidLength, Digest};
use sha3::{digest::InvalidLength, Digest, Sha3_256};
use std::str;

use super::constants::V1_X25519_SIGNATURE_HASH_SALT;
// struct DefaultCipherSuite;
// impl CipherSuite for DefaultCipherSuite {
// type OprfCs = opaque_ke::Ristretto255;
Expand Down Expand Up @@ -76,104 +79,46 @@ pub enum EncryptionSetupError {
* TODO: Fuzz input
* MUST BE DETERMINISTIC
*/
pub fn setup(password: &str, email: &str) -> Result<ClientSecrets, EncryptionSetupError> {
let master_key = argon2id_hash_password(password, email, V1_ENCRYPTION_PASSWORD_SALT)?;
let seed = ed25519_compact::Seed::from_slice(master_key[0..32].as_bytes()).unwrap();
let ed25519_keypair = ed25519_compact::KeyPair::from_seed(seed); //from_slice(master_key.as_bytes().take).unwrap();
Ok(ClientSecrets {
master_key,
ed25519_keypair,
pub fn setup(password: &str, email: &str) -> Result<MasterKey, EncryptionSetupError> {
let salt = SaltString::from_b64(&V1_ENCRYPTION_PASSWORD_SALT).map_err(PasswordHashErrors::PasswordHashError)?;
let master_key = argon2id_hash_password(password, email, salt.as_salt())?
.hash
.unwrap();
Ok(MasterKey {
0: master_key.to_string(),
})
}



pub fn create_ed25519_signing_keys(master_key: &MasterKey) -> Result<ed25519_compact::KeyPair, ed25519_compact::Error>{
let mut hasher = Sha3_256::new();
hasher.update(master_key.0.as_bytes());
hasher.update(V1_X25519_SIGNATURE_HASH_SALT);
let slice = hasher.finalize();
let seed = ed25519_compact::Seed::from_slice(&slice).unwrap(); //[0..32]
let ed25519_key_pair = ed25519_compact::KeyPair::from_seed(seed);
Ok(ed25519_key_pair)
}

pub fn generate_bucket_encryption_key(
secrets: Arc<ClientSecrets>,
master_key: Arc<MasterKey>,
bucket_id: &uuid::Uuid,
) -> Result<aes_gcm::Aes256Gcm, InvalidLength> {
let bucket_key = bucket_key_hash_sha256(secrets.master_key.clone(), bucket_id);
let bucket_key = bucket_key_hash_sha256(&master_key, bucket_id);
//let aes_gcm_key = aes_gcm::Key::<aes_gcm::Aes256Gcm>::from_slice(bucket_key.as_slice());
let aes_gcm_key = aes_gcm::Aes256Gcm::new_from_slice(bucket_key.as_slice());
aes_gcm_key
}

#[derive(zeroize::Zeroize, Clone)]
pub struct ClientSecrets {
master_key: String,
pub ed25519_keypair: ed25519_compact::KeyPair,
}

impl ClientSecrets {
pub fn get_ed25519_public_signing_key(&self) -> ed25519_compact::PublicKey {
self.ed25519_keypair.pk
}
#[derive(zeroize::Zeroize)]
pub struct Secrets {
pub master_key: MasterKey,
pub signing_key: ed25519_compact::KeyPair,
}

/*
Use the aes_gcm symetric key to encrypt the file content.
Use HighwayHash to hash the encrypted file content.
Use the ed25519 keypair to sign the hash.
*/
pub async fn encrypted_upload_files(
aes_gcm_symmetric_key: &aes_gcm::Key<aes_gcm::Aes256Gcm>,
ed25519_signing_key: &ed25519_compact::KeyPair,
file: gloo::file::File,
upload_fn: fn(&[u8]),
upload_finish: fn(&[u8]),
) {
let key = highway::Key([1, 2, 3, 4]);
let mut highway_hash = highway::HighwayHasher::new(key);
let filename = file.name();
let aes_gcm_cipher = aes_gcm::Aes256Gcm::new_from_slice(aes_gcm_symmetric_key).unwrap();
let nonce = aes_gcm::Nonce::from_slice(filename.as_bytes()); //TODO: Fix
//let mut file_bytes = file.bytes();
//file.read_to_end(&mut file_bytes);
//TODO: Chunk it. Read 1MB at a time.
//let file_reader = gloo::file::futures::read_as_array_bytes(&file, read_fn);
let file_size = file.size();
// Iterate over the file in 1MB chunks,
// encrypt each chunk while also hashing it.
// Then upload the encrypted chunk.
// After the file is uploaded, sign the hash, upload the hash signature.
// Done!
for _it in 0..&file_size / 1024 {
let chunk_data = gloo::file::futures::read_as_bytes(&file).await.unwrap(); //TODO: What if file is too big for memory?
let ciphertext = aes_gcm_cipher
.encrypt(nonce, chunk_data.as_slice())
.unwrap();
highway_hash.append(&ciphertext);
upload_fn(&ciphertext);
}
let hash = highway_hash.finalize256();
let signature = ed25519_signing_key.sk.sign(bytemuck::bytes_of(&hash), None);
// Upload
upload_finish(signature.as_slice());
}

pub async fn encrypt_chunk(
aes_gcm_symmetric_key: &aes_gcm::Key<aes_gcm::Aes256Gcm>,
nonce: &Nonce<typenum::U12>,
_ed25519_signing_key: &ed25519_compact::KeyPair,
highway_hash: &mut highway::HighwayHasher,
chunk_data: Vec<u8>,
) -> Vec<u8> {
let aes_gcm_cipher = aes_gcm::Aes256Gcm::new(aes_gcm_symmetric_key);
let ciphertext = aes_gcm_cipher
.encrypt(nonce, chunk_data.as_slice())
.unwrap();
highway_hash.append(&ciphertext);
ciphertext
}

// pub async fn encrypt_finalize(
// ed25519_signing_key: &ed25519_compact::KeyPair,
// highway_hash: &mut highway::HighwayHasher,
// ) -> Signature {
// let hash = highway_hash.finalize256();
// let signature = ed25519_signing_key.sk.sign(&bytemuck::bytes_of(&hash), None);
// signature
// }
#[derive(zeroize::Zeroize, Clone)]
pub struct MasterKey (pub String);

//pub fn generate_rsa_keypair()

#[cfg(test)]
mod tests {
Expand Down
Loading

0 comments on commit e577af8

Please sign in to comment.