test: e2e for encrypted sync

This commit is contained in:
zhom
2026-03-16 02:57:31 +04:00
parent b4488ee3ec
commit 140e611085
+207
View File
@@ -841,3 +841,210 @@ async fn test_profile_bypass_rules_sync() {
client.delete(&test_key, None).await.unwrap();
client.delete(&empty_key, None).await.unwrap();
}
#[tokio::test]
async fn test_encrypted_profile_sync() {
use donutbrowser_lib::sync::encryption::{
decrypt_bytes, derive_profile_key, encrypt_bytes, generate_salt,
};
ensure_sync_server_available().await;
let client = TestClient::new();
let temp_dir = TempDir::new().unwrap();
let profile_id = uuid::Uuid::new_v4().to_string();
let test_key = format!("profiles/{}.tar.gz.enc", profile_id);
let bundle = create_test_profile_bundle(temp_dir.path());
let salt = generate_salt();
let password = "test-e2e-encryption-password";
let key = derive_profile_key(password, &salt).unwrap();
let encrypted = encrypt_bytes(&key, &bundle).unwrap();
assert_ne!(
encrypted, bundle,
"Encrypted data should differ from plaintext"
);
assert!(
encrypted.len() > bundle.len(),
"Encrypted data includes nonce + auth tag overhead"
);
let presign = client
.presign_upload(&test_key, "application/octet-stream")
.await
.unwrap();
client
.upload_bytes(&presign.url, &encrypted, "application/octet-stream")
.await
.unwrap();
let stat = client.stat(&test_key).await.unwrap();
assert!(stat.exists);
assert_eq!(stat.size, Some(encrypted.len() as u64));
let download_presign = client.presign_download(&test_key).await.unwrap();
let downloaded = client.download_bytes(&download_presign.url).await.unwrap();
assert_eq!(downloaded.len(), encrypted.len());
let decrypted = decrypt_bytes(&key, &downloaded).unwrap();
assert_eq!(
decrypted, bundle,
"Decrypted content should match original bundle"
);
let extract_dir = temp_dir.path().join("extracted");
fs::create_dir_all(&extract_dir).unwrap();
let metadata = extract_bundle(&decrypted, &extract_dir);
assert_eq!(metadata["id"], "test-profile-id");
assert_eq!(metadata["name"], "Test Profile");
assert_eq!(metadata["browser"], "chromium");
assert_eq!(metadata["version"], "120.0.0");
assert!(metadata["sync_enabled"].as_bool().unwrap());
let tags = metadata["tags"].as_array().unwrap();
assert_eq!(tags.len(), 2);
assert_eq!(tags[0], "test");
assert_eq!(tags[1], "e2e");
let test_file = extract_dir.join("profile").join("test_file.txt");
assert!(test_file.exists());
assert_eq!(fs::read_to_string(test_file).unwrap(), "test content");
let wrong_key = derive_profile_key("wrong-password", &salt).unwrap();
assert!(
decrypt_bytes(&wrong_key, &downloaded).is_err(),
"Decryption with wrong key should fail"
);
let different_salt = generate_salt();
let wrong_salt_key = derive_profile_key(password, &different_salt).unwrap();
assert!(
decrypt_bytes(&wrong_salt_key, &downloaded).is_err(),
"Decryption with key derived from wrong salt should fail"
);
client.delete(&test_key, None).await.unwrap();
let final_stat = client.stat(&test_key).await.unwrap();
assert!(!final_stat.exists);
}
#[tokio::test]
async fn test_encrypted_delta_sync() {
use donutbrowser_lib::sync::encryption::{
decrypt_bytes, derive_profile_key, encrypt_bytes, generate_salt,
};
ensure_sync_server_available().await;
let client = TestClient::new();
let profile_id = uuid::Uuid::new_v4().to_string();
let salt = generate_salt();
let password = "delta-sync-test-password";
let key = derive_profile_key(password, &salt).unwrap();
let file1_key = format!("profiles/{}/files/file1.txt.enc", profile_id);
let file2_key = format!("profiles/{}/files/file2.txt.enc", profile_id);
let file3_key = format!("profiles/{}/files/file3.txt.enc", profile_id);
let content1 = b"file one content";
let content2 = b"file two content";
let content3 = b"file three content";
let encrypted1 = encrypt_bytes(&key, content1).unwrap();
let encrypted2 = encrypt_bytes(&key, content2).unwrap();
let encrypted3 = encrypt_bytes(&key, content3).unwrap();
let presign1 = client
.presign_upload(&file1_key, "application/octet-stream")
.await
.unwrap();
client
.upload_bytes(&presign1.url, &encrypted1, "application/octet-stream")
.await
.unwrap();
let presign2 = client
.presign_upload(&file2_key, "application/octet-stream")
.await
.unwrap();
client
.upload_bytes(&presign2.url, &encrypted2, "application/octet-stream")
.await
.unwrap();
let presign3 = client
.presign_upload(&file3_key, "application/octet-stream")
.await
.unwrap();
client
.upload_bytes(&presign3.url, &encrypted3, "application/octet-stream")
.await
.unwrap();
for (file_key, expected_content) in [
(&file1_key, content1.as_slice()),
(&file2_key, content2.as_slice()),
(&file3_key, content3.as_slice()),
] {
let dl_presign = client.presign_download(file_key).await.unwrap();
let downloaded = client.download_bytes(&dl_presign.url).await.unwrap();
let decrypted = decrypt_bytes(&key, &downloaded).unwrap();
assert_eq!(
decrypted, expected_content,
"Decrypted content mismatch for {file_key}"
);
}
let stat1_before = client.stat(&file1_key).await.unwrap();
let stat2_before = client.stat(&file2_key).await.unwrap();
let stat3_before = client.stat(&file3_key).await.unwrap();
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let updated_content2 = b"file two content -- updated with new data";
let encrypted2_updated = encrypt_bytes(&key, updated_content2).unwrap();
let presign2_update = client
.presign_upload(&file2_key, "application/octet-stream")
.await
.unwrap();
client
.upload_bytes(
&presign2_update.url,
&encrypted2_updated,
"application/octet-stream",
)
.await
.unwrap();
let stat2_after = client.stat(&file2_key).await.unwrap();
assert_ne!(
stat2_before.size, stat2_after.size,
"File2 size should have changed after update"
);
let stat1_after = client.stat(&file1_key).await.unwrap();
let stat3_after = client.stat(&file3_key).await.unwrap();
assert_eq!(
stat1_before.size, stat1_after.size,
"File1 should be unchanged"
);
assert_eq!(
stat3_before.size, stat3_after.size,
"File3 should be unchanged"
);
let dl_presign2 = client.presign_download(&file2_key).await.unwrap();
let downloaded2 = client.download_bytes(&dl_presign2.url).await.unwrap();
let decrypted2 = decrypt_bytes(&key, &downloaded2).unwrap();
assert_eq!(
decrypted2,
updated_content2.to_vec(),
"Updated file2 should decrypt to new content"
);
client.delete(&file1_key, None).await.unwrap();
client.delete(&file2_key, None).await.unwrap();
client.delete(&file3_key, None).await.unwrap();
}