Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3871512
WIP: build(deps): upgrade Tauri from `beta` to `rc`
daniel-mader Aug 28, 2024
75fb47a
Merge branch 'dev' into build/upgrade-tauri
daniel-mader Nov 18, 2024
9855259
build: remove unused dependency
daniel-mader Nov 18, 2024
7d4b970
ci: bump all `tauri` dependencies
daniel-mader Nov 18, 2024
b7e04ab
chore: update developer info
daniel-mader Nov 19, 2024
8440f5d
Merge branch 'dev' into build/upgrade-tauri
daniel-mader Jan 3, 2025
73e04cd
build: bump tauri versions
daniel-mader Jan 5, 2025
b08a644
feat: migrate `fs` permissions to access `/assets` folder
daniel-mader Jan 5, 2025
f394385
build: use default port for frontend dev server
daniel-mader Jan 5, 2025
ca9683b
build(js): bump tauri plugins
daniel-mader Jan 5, 2025
55b724b
feat: use `cloud-storage` plugin
daniel-mader Jan 5, 2025
48291ed
feat: verbose usage of cloud storage
daniel-mader Jan 8, 2025
c4c402c
feat: refresh after interaction with backup, trigger functions on toggle
daniel-mader Jan 9, 2025
cfcc8fe
build: align app identifier across targets
daniel-mader Jan 9, 2025
b1e0f3f
refactor: use plugin args
daniel-mader Jan 11, 2025
e2e41bc
feat: implement UI for profile recovery
daniel-mader Jan 14, 2025
9b59e67
feat: create backup file from backend
daniel-mader Jan 15, 2025
d0b8a60
WIP: recovery UX flow
daniel-mader Jan 15, 2025
400d415
Merge branch 'dev' into feat/cloud-backup
daniel-mader Feb 7, 2025
dcd9365
build(deps): run `pnpm update`
daniel-mader Feb 7, 2025
12ba092
feat: change icon, handle no backups
daniel-mader Feb 12, 2025
75e5b57
Merge branch 'dev' into feat/cloud-backup
daniel-mader Mar 19, 2025
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
1,329 changes: 674 additions & 655 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions identity-wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ rust-version.workspace = true

[dependencies]
tauri.workspace = true
tauri-plugin-cloud-storage = { path = "../../../daniel-mader/plugins-workspace/plugins/tauri-plugin-cloud-storage" }
tauri-plugin-fs = { version = "2" }

aes-gcm = "0.10.3"
anyhow = "1.0"
argon2 = "0.5.3"
async-trait = "0.1"
base64 = "0.22"
chrono = "0.4"
Expand Down Expand Up @@ -41,6 +45,7 @@ reqwest = { version = "0.11", default-features = false, features = [
serde = { version = "1.0", features = ["derive"] }
serde_with = "3.8"
serde_json.workspace = true
sha2 = "0.10.8"
sha256 = "1.4"
stronghold_engine = { version = "2.0.1" }
stronghold_ext = { git = "https://github.com/tensor-programming/stronghold_ext", features = ["crypto"] }
Expand Down
3 changes: 2 additions & 1 deletion identity-wallet/bindings/actions/Action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AddRecentSearch } from "./AddRecentSearch";
import type { AddTrustList } from "./AddTrustList";
import type { AddTrustListEntry } from "./AddTrustListEntry";
import type { CancelUserFlow } from "./CancelUserFlow";
import type { CreateBackup } from "./CreateBackup";
import type { CreateNew } from "./CreateNew";
import type { CredentialOffersSelected } from "./CredentialOffersSelected";
import type { CredentialsSelected } from "./CredentialsSelected";
Expand All @@ -24,4 +25,4 @@ import type { UpdateCredentialMetadata } from "./UpdateCredentialMetadata";
import type { UpdateProfileSettings } from "./UpdateProfileSettings";
import type { UpdateSortingPreference } from "./UpdateSortingPreference";

export type Action = { "type": "[App] Get state" } | { "type": "[Storage] Unlock", payload: UnlockStorage, } | { "type": "[App] Reset" } | { "type": "[DID] Create new", payload: CreateNew, } | { "type": "[Settings] Set locale", payload: SetLocale, } | { "type": "[Settings] Update profile", payload: UpdateProfileSettings, } | { "type": "[QR Code] Scanned", payload: QrCodeScanned, } | { "type": "[Authenticate] Connection accepted" } | { "type": "[User Flow] Cancel", payload?: CancelUserFlow, } | { "type": "[DEV] Load DEV profile", payload: DevProfile, } | { "type": "[DEV] Toggle DEV mode" } | { "type": "[Authenticate] Credentials selected", payload: CredentialsSelected, } | { "type": "[Credential Offer] Selected", payload: CredentialOffersSelected, } | { "type": "[Credential Metadata] Update", payload: UpdateCredentialMetadata, } | { "type": "[Credential] Delete", payload: DeleteCredential, } | { "type": "[User Journey] Cancel" } | { "type": "[Settings] Update sorting preference", payload: UpdateSortingPreference, } | { "type": "[Search] Query", payload: SearchQuery, } | { "type": "[Search] Add recent", payload: AddRecentSearch, } | { "type": "[Search] Delete recent", payload: DeleteRecentSearch, } | { "type": "[DID] Set preferred method", payload: SetPreferredDidMethod, } | { "type": "[Keys] Set preferred key type", payload: SetPreferredKeyType, } | { "type": "[Trust List] Add entry", payload: AddTrustListEntry, } | { "type": "[Trust List] Edit entry", payload: EditTrustListEntry, } | { "type": "[Trust List] Delete entry", payload: DeleteTrustListEntry, } | { "type": "[Trust List] Toggle entry", payload: ToggleTrustListEntry, } | { "type": "[Trust Lists] Add", payload: AddTrustList, } | { "type": "[Trust Lists] Edit", payload: EditTrustList, } | { "type": "[Trust Lists] Delete", payload: DeleteTrustList, };
export type Action = { "type": "[App] Get state" } | { "type": "[Storage] Unlock", payload: UnlockStorage, } | { "type": "[App] Reset" } | { "type": "[DID] Create new", payload: CreateNew, } | { "type": "[Settings] Set locale", payload: SetLocale, } | { "type": "[Settings] Update profile", payload: UpdateProfileSettings, } | { "type": "[QR Code] Scanned", payload: QrCodeScanned, } | { "type": "[Authenticate] Connection accepted" } | { "type": "[User Flow] Cancel", payload?: CancelUserFlow, } | { "type": "[DEV] Load DEV profile", payload: DevProfile, } | { "type": "[DEV] Toggle DEV mode" } | { "type": "[Authenticate] Credentials selected", payload: CredentialsSelected, } | { "type": "[Credential Offer] Selected", payload: CredentialOffersSelected, } | { "type": "[Credential Metadata] Update", payload: UpdateCredentialMetadata, } | { "type": "[Credential] Delete", payload: DeleteCredential, } | { "type": "[User Journey] Cancel" } | { "type": "[Settings] Update sorting preference", payload: UpdateSortingPreference, } | { "type": "[Search] Query", payload: SearchQuery, } | { "type": "[Search] Add recent", payload: AddRecentSearch, } | { "type": "[Search] Delete recent", payload: DeleteRecentSearch, } | { "type": "[DID] Set preferred method", payload: SetPreferredDidMethod, } | { "type": "[Keys] Set preferred key type", payload: SetPreferredKeyType, } | { "type": "[Trust List] Add entry", payload: AddTrustListEntry, } | { "type": "[Trust List] Edit entry", payload: EditTrustListEntry, } | { "type": "[Trust List] Delete entry", payload: DeleteTrustListEntry, } | { "type": "[Trust List] Toggle entry", payload: ToggleTrustListEntry, } | { "type": "[Trust Lists] Add", payload: AddTrustList, } | { "type": "[Trust Lists] Edit", payload: EditTrustList, } | { "type": "[Trust Lists] Delete", payload: DeleteTrustList, } | { "type": "[Backup] Create", payload: CreateBackup, };
3 changes: 3 additions & 0 deletions identity-wallet/bindings/actions/CreateBackup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export interface CreateBackup { path: string, password: string, }
3 changes: 3 additions & 0 deletions identity-wallet/src/state/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ mod bindings {
use ts_rs::TS;

use crate::state::{
backup::actions::create::CreateBackup,
common::actions::{cancel_user_flow::CancelUserFlow, unlock_storage::UnlockStorage},
credentials::actions::{
credential_offers_selected::CredentialOffersSelected, credentials_selected::CredentialsSelected,
Expand Down Expand Up @@ -153,5 +154,7 @@ mod bindings {
TrustListsEdit { payload: EditTrustList },
#[serde(rename = "[Trust Lists] Delete")]
TrustListsDelete { payload: DeleteTrustList },
#[serde(rename = "[Backup] Create")]
CreateBackup { payload: CreateBackup },
}
}
32 changes: 32 additions & 0 deletions identity-wallet/src/state/backup/actions/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::{fmt::Formatter, path::PathBuf};

use serde::{Deserialize, Serialize};
use ts_rs::TS;

use crate::{
reducer,
state::{actions::ActionTrait, backup::reducers::create::create_backup, Reducer},
};

#[derive(Serialize, Deserialize, Clone, TS)]
#[ts(export, export_to = "bindings/actions/CreateBackup.ts")]
pub struct CreateBackup {
pub path: String,
pub password: String,
}

impl std::fmt::Debug for CreateBackup {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CreateBackup")
.field("path", &self.path)
.field("password", &"*****")
.finish()
}
}

#[typetag::serde(name = "[Backup] Create")]
impl ActionTrait for CreateBackup {
fn reducers<'a>(&self) -> Vec<Reducer<'a>> {
vec![reducer!(create_backup)]
}
}
16 changes: 16 additions & 0 deletions identity-wallet/src/state/backup/actions/enable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};

use crate::{
reducer,
state::{actions::ActionTrait, backup::reducers::enable::enable_backup, Reducer},
};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EnableBackup;

#[typetag::serde(name = "[Backup] Enable")]
impl ActionTrait for EnableBackup {
fn reducers<'a>(&self) -> Vec<Reducer<'a>> {
vec![reducer!(enable_backup)]
}
}
2 changes: 2 additions & 0 deletions identity-wallet/src/state/backup/actions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod create;
pub mod enable;
2 changes: 2 additions & 0 deletions identity-wallet/src/state/backup/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod actions;
pub mod reducers;
53 changes: 53 additions & 0 deletions identity-wallet/src/state/backup/reducers/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::fs;

use aes_gcm::{aead::Aead, Aes256Gcm, Key, KeyInit};
use argon2::Argon2;
use log::info;
use sha2::{Digest, Sha256};
use tauri_plugin_fs;

use crate::{
error::AppError,
persistence::{STATE_FILE, STRONGHOLD},
state::{
actions::{listen, Action},
backup::actions::create::CreateBackup,
AppState,
},
};

pub async fn create_backup(state: AppState, action: Action) -> Result<AppState, AppError> {
if let Some(CreateBackup { path, password }) = listen::<CreateBackup>(action) {
// 1. Hash the password using SHA-256
// TODO: prefer Argon2? Problem: where do we store the salt?
let mut hasher = Sha256::new();
hasher.update(password.as_bytes());
let key = hasher.finalize();

// 2. Use the hashed password as key for AES-256-GCM
let cipher = Aes256Gcm::new(&key);

// 3. TODO: Generate a random nonce (problem: how to store it?)
let nonce = [0; 12]; // 96 bits

let state_file = fs::read(STATE_FILE.lock().unwrap().as_path())?;
let stronghold_file = fs::read(STRONGHOLD.lock().unwrap().as_path())?;
// TODO: also backup ASSETS_DIR

let data = vec![state_file, stronghold_file].concat();

let res = cipher
.encrypt(&nonce.into(), data.as_ref())
.map_err(|_| AppError::Error("Encryption failed".to_string()))?;

info!("Writing backup to file: {} ({} bytes)", path, data.len());

fs::write(path, res)?;

return Ok(AppState {
current_user_prompt: None,
..state
});
}
Ok(state)
}
15 changes: 15 additions & 0 deletions identity-wallet/src/state/backup/reducers/enable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::{
error::AppError,
state::{actions::Action, AppState},
};

use log::info;
// use tauri_plugin_cloud_storage::CloudStorageExt;

pub async fn enable_backup(state: AppState, _action: Action) -> Result<AppState, AppError> {
// tauri_plugin_cloud_storage::CloudStorageExt::cloud_storage(&self).ping(PingRequest {
// value: Some("ping".to_string()),
// })?;
// info!("response: {:?}", response);
Ok(AppState { ..state })
}
2 changes: 2 additions & 0 deletions identity-wallet/src/state/backup/reducers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod create;
pub mod enable;
1 change: 1 addition & 0 deletions identity-wallet/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod actions;
pub mod backup;
pub mod common;
pub mod connections;
pub mod core_utils;
Expand Down
Loading